1/*	$OpenBSD: courier.c,v 1.15 2006/03/17 19:17:13 moritz Exp $	*/
2/*	$NetBSD: courier.c,v 1.7 1997/02/11 09:24:16 mrg Exp $	*/
3
4/*-
5 * SPDX-License-Identifier: BSD-3-Clause
6 *
7 * Copyright (c) 1986, 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 Courier modem.
37 * Derived from Hayes driver.
38 */
39#include "tip.h"
40#include <sys/ioctl.h>
41#include <stdio.h>
42
43#define	MAXRETRY	5
44
45static	int dialtimeout = 0;
46static	int connected = 0;
47static	jmp_buf timeoutbuf;
48
49static void	sigALRM(int);
50static int	cour_swallow(char *);
51static int	cour_connect(void);
52static int	coursync(void);
53static void	cour_write(int, char *, int);
54static void	cour_nap(void);
55#ifdef DEBUG
56static void	cour_verbose_read(void);
57#endif
58
59int
60cour_dialer(char *num, char *acu)
61{
62	char *cp;
63#ifdef ACULOG
64	char line[80];
65#endif
66	struct termios cntrl;
67
68	if (boolean(value(VERBOSE)))
69		printf("Using \"%s\"\n", acu);
70
71	tcgetattr(FD, &cntrl);
72	cntrl.c_cflag |= HUPCL;
73	tcsetattr(FD, TCSAFLUSH, &cntrl);
74	/*
75	 * Get in synch.
76	 */
77	if (!coursync()) {
78badsynch:
79		printf("can't synchronize with courier\n");
80#ifdef ACULOG
81		logent(value(HOST), num, "courier", "can't synch up");
82#endif
83		return (0);
84	}
85	cour_write(FD, "AT E0\r", 6);	/* turn off echoing */
86	sleep(1);
87#ifdef DEBUG
88	if (boolean(value(VERBOSE)))
89		cour_verbose_read();
90#endif
91	tcflush(FD, TCIOFLUSH);
92	cour_write(FD, "AT C1 E0 H0 Q0 X6 V1\r", 21);
93	if (!cour_swallow("\r\nOK\r\n"))
94		goto badsynch;
95	fflush(stdout);
96	cour_write(FD, "AT D", 4);
97	for (cp = num; *cp; cp++)
98		if (*cp == '=')
99			*cp = ',';
100	cour_write(FD, num, strlen(num));
101	cour_write(FD, "\r", 1);
102	connected = cour_connect();
103#ifdef ACULOG
104	if (dialtimeout) {
105		(void)snprintf(line, sizeof line, "%ld second dial timeout",
106			number(value(DIALTIMEOUT)));
107		logent(value(HOST), num, "cour", line);
108	}
109#endif
110	if (dialtimeout)
111		cour_disconnect();
112	return (connected);
113}
114
115void
116cour_disconnect(void)
117{
118	 /* first hang up the modem*/
119	ioctl(FD, TIOCCDTR, 0);
120	sleep(1);
121	ioctl(FD, TIOCSDTR, 0);
122	coursync();				/* reset */
123	close(FD);
124}
125
126void
127cour_abort(void)
128{
129	cour_write(FD, "\r", 1);	/* send anything to abort the call */
130	cour_disconnect();
131}
132
133/*ARGSUSED*/
134static void
135sigALRM(int signo)
136{
137	printf("\07timeout waiting for reply\n");
138	dialtimeout = 1;
139	longjmp(timeoutbuf, 1);
140}
141
142static int
143cour_swallow(char *match)
144{
145	sig_t f;
146	char c;
147
148	f = signal(SIGALRM, sigALRM);
149	dialtimeout = 0;
150	do {
151		if (*match =='\0') {
152			signal(SIGALRM, f);
153			return (1);
154		}
155		if (setjmp(timeoutbuf)) {
156			signal(SIGALRM, f);
157			return (0);
158		}
159		alarm(number(value(DIALTIMEOUT)));
160		read(FD, &c, 1);
161		alarm(0);
162		c &= 0177;
163#ifdef DEBUG
164		if (boolean(value(VERBOSE)))
165			putchar(c);
166#endif
167	} while (c == *match++);
168#ifdef DEBUG
169	if (boolean(value(VERBOSE)))
170		fflush(stdout);
171#endif
172	signal(SIGALRM, SIG_DFL);
173	return (0);
174}
175
176struct baud_msg {
177	char *msg;
178	int baud;
179} baud_msg[] = {
180	{ "",		B300 },
181	{ " 1200",	B1200 },
182	{ " 2400",	B2400 },
183	{ " 9600",	B9600 },
184	{ " 9600/ARQ",	B9600 },
185	{ 0,		0 },
186};
187
188static int
189cour_connect(void)
190{
191	char c;
192	int nc, nl, n;
193	char dialer_buf[64];
194	struct baud_msg *bm;
195	sig_t f;
196
197	if (cour_swallow("\r\n") == 0)
198		return (0);
199	f = signal(SIGALRM, sigALRM);
200again:
201	nc = 0; nl = sizeof(dialer_buf)-1;
202	bzero(dialer_buf, sizeof(dialer_buf));
203	dialtimeout = 0;
204	for (nc = 0, nl = sizeof(dialer_buf)-1 ; nl > 0 ; nc++, nl--) {
205		if (setjmp(timeoutbuf))
206			break;
207		alarm(number(value(DIALTIMEOUT)));
208		n = read(FD, &c, 1);
209		alarm(0);
210		if (n <= 0)
211			break;
212		c &= 0x7f;
213		if (c == '\r') {
214			if (cour_swallow("\n") == 0)
215				break;
216			if (!dialer_buf[0])
217				goto again;
218			if (strcmp(dialer_buf, "RINGING") == 0 &&
219			    boolean(value(VERBOSE))) {
220#ifdef DEBUG
221				printf("%s\r\n", dialer_buf);
222#endif
223				goto again;
224			}
225			if (strncmp(dialer_buf, "CONNECT",
226				    sizeof("CONNECT")-1) != 0)
227				break;
228			for (bm = baud_msg ; bm->msg ; bm++)
229				if (strcmp(bm->msg,
230				    dialer_buf+sizeof("CONNECT")-1) == 0) {
231					struct termios	cntrl;
232
233					tcgetattr(FD, &cntrl);
234					cfsetospeed(&cntrl, bm->baud);
235					cfsetispeed(&cntrl, bm->baud);
236					tcsetattr(FD, TCSAFLUSH, &cntrl);
237					signal(SIGALRM, f);
238#ifdef DEBUG
239					if (boolean(value(VERBOSE)))
240						printf("%s\r\n", dialer_buf);
241#endif
242					return (1);
243				}
244			break;
245		}
246		dialer_buf[nc] = c;
247#ifdef notdef
248		if (boolean(value(VERBOSE)))
249			putchar(c);
250#endif
251	}
252	printf("%s\r\n", dialer_buf);
253	signal(SIGALRM, f);
254	return (0);
255}
256
257/*
258 * This convoluted piece of code attempts to get
259 * the courier in sync.
260 */
261static int
262coursync(void)
263{
264	int already = 0;
265	int len;
266	char buf[40];
267
268	while (already++ < MAXRETRY) {
269		tcflush(FD, TCIOFLUSH);
270		cour_write(FD, "\rAT Z\r", 6);	/* reset modem */
271		bzero(buf, sizeof(buf));
272		sleep(1);
273		ioctl(FD, FIONREAD, &len);
274		if (len) {
275			len = read(FD, buf, sizeof(buf));
276#ifdef DEBUG
277			buf[len] = '\0';
278			printf("coursync: (\"%s\")\n\r", buf);
279#endif
280			if (strchr(buf, '0') ||
281		   	   (strchr(buf, 'O') && strchr(buf, 'K')))
282				return(1);
283		}
284		/*
285		 * If not strapped for DTR control,
286		 * try to get command mode.
287		 */
288		sleep(1);
289		cour_write(FD, "+++", 3);
290		sleep(1);
291		/*
292		 * Toggle DTR to force anyone off that might have left
293		 * the modem connected.
294		 */
295		ioctl(FD, TIOCCDTR, 0);
296		sleep(1);
297		ioctl(FD, TIOCSDTR, 0);
298	}
299	cour_write(FD, "\rAT Z\r", 6);
300	return (0);
301}
302
303static void
304cour_write(int fd, char *cp, int n)
305{
306#ifdef notdef
307	if (boolean(value(VERBOSE)))
308		write(1, cp, n);
309#endif
310	tcdrain(fd);
311	cour_nap();
312	for ( ; n-- ; cp++) {
313		write(fd, cp, 1);
314		tcdrain(fd);
315		cour_nap();
316	}
317}
318
319#ifdef DEBUG
320static void
321cour_verbose_read(void)
322{
323	int n = 0;
324	char buf[BUFSIZ];
325
326	if (ioctl(FD, FIONREAD, &n) < 0)
327		return;
328	if (n <= 0)
329		return;
330	if (read(FD, buf, n) != n)
331		return;
332	write(1, buf, n);
333}
334#endif
335
336/* Give the courier 50 milliseconds between characters */
337static void
338cour_nap(void)
339{
340	struct timespec ts;
341
342	ts.tv_sec = 0;
343	ts.tv_nsec = 50 * 1000000;
344
345	nanosleep(&ts, NULL);
346}
347