xref: /netbsd-src/usr.bin/tip/aculib/courier.c (revision 23c8222edbfb0f0932d88a8351d3a0cf817dfb9e)
1 /*	$NetBSD: courier.c,v 1.13 2004/04/23 22:11:44 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1986, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)courier.c	8.1 (Berkeley) 6/6/93";
36 #endif
37 __RCSID("$NetBSD: courier.c,v 1.13 2004/04/23 22:11:44 christos Exp $");
38 #endif /* not lint */
39 
40 /*
41  * Routines for calling up on a Courier modem.
42  * Derived from Hayes driver.
43  */
44 #include "tip.h"
45 
46 #define	MAXRETRY	5
47 
48 static	int timeout = 0;
49 static	int connected = 0;
50 static	jmp_buf timeoutbuf;
51 
52 static	int	cour_connect __P((void));
53 static	void	cour_nap __P((void));
54 static	void	cour_napx __P((int));
55 static	int	cour_swallow __P((const char *));
56 static	int	coursync __P((void));
57 #ifdef DEBUG
58 static	void	cour_verbose_read __P((void));
59 #endif
60 static	void	cour_write __P((int, const char *, int));
61 static	void	sigALRM __P((int));
62 
63 int
64 cour_dialer(num, acu)
65 	char *num;
66 	char *acu;
67 {
68 	char *cp;
69 #ifdef ACULOG
70 	char line[80];
71 #endif
72 	struct termios cntrl;
73 
74 	if (boolean(value(VERBOSE)))
75 		printf("Using \"%s\"\n", acu);
76 
77 	tcgetattr(FD, &cntrl);
78 	cntrl.c_cflag |= HUPCL;
79 	tcsetattr(FD, TCSAFLUSH, &cntrl);
80 	/*
81 	 * Get in synch.
82 	 */
83 	if (!coursync()) {
84 badsynch:
85 		printf("can't synchronize with courier\n");
86 #ifdef ACULOG
87 		logent(value(HOST), num, "courier", "can't synch up");
88 #endif
89 		return (0);
90 	}
91 	cour_write(FD, "AT E0\r", 6);	/* turn off echoing */
92 	sleep(1);
93 #ifdef DEBUG
94 	if (boolean(value(VERBOSE)))
95 		cour_verbose_read();
96 #endif
97 	tcflush(FD, TCIOFLUSH);
98 	cour_write(FD, "AT C1 E0 H0 Q0 X6 V1\r", 21);
99 	if (!cour_swallow("\r\nOK\r\n"))
100 		goto badsynch;
101 	fflush(stdout);
102 	cour_write(FD, "AT D", 4);
103 	for (cp = num; *cp; cp++)
104 		if (*cp == '=')
105 			*cp = ',';
106 	cour_write(FD, num, strlen(num));
107 	cour_write(FD, "\r", 1);
108 	connected = cour_connect();
109 #ifdef ACULOG
110 	if (timeout) {
111 		(void)snprintf(line, sizeof line, "%d second dial timeout",
112 			(int)number(value(DIALTIMEOUT)));
113 		logent(value(HOST), num, "cour", line);
114 	}
115 #endif
116 	if (timeout)
117 		cour_disconnect();
118 	return (connected);
119 }
120 
121 void
122 cour_disconnect()
123 {
124 
125 	/* first hang up the modem*/
126 	ioctl(FD, TIOCCDTR, 0);
127 	sleep(1);
128 	ioctl(FD, TIOCSDTR, 0);
129 	coursync();				/* reset */
130 	close(FD);
131 }
132 
133 void
134 cour_abort()
135 {
136 
137 	cour_write(FD, "\r", 1);	/* send anything to abort the call */
138 	cour_disconnect();
139 }
140 
141 static void
142 sigALRM(dummy)
143 	int dummy;
144 {
145 
146 	printf("\07timeout waiting for reply\n");
147 	timeout = 1;
148 	longjmp(timeoutbuf, 1);
149 }
150 
151 static int
152 cour_swallow(match)
153 	const char *match;
154 {
155 	sig_t f;
156 	char c;
157 
158 #if __GNUC__	/* XXX pacify gcc */
159 	(void)&match;
160 #endif
161 
162 	f = signal(SIGALRM, sigALRM);
163 	timeout = 0;
164 	do {
165 		if (*match =='\0') {
166 			signal(SIGALRM, f);
167 			return (1);
168 		}
169 		if (setjmp(timeoutbuf)) {
170 			signal(SIGALRM, f);
171 			return (0);
172 		}
173 		alarm(number(value(DIALTIMEOUT)));
174 		read(FD, &c, 1);
175 		alarm(0);
176 		c &= 0177;
177 #ifdef DEBUG
178 		if (boolean(value(VERBOSE)))
179 			putchar(c);
180 #endif
181 	} while (c == *match++);
182 #ifdef DEBUG
183 	if (boolean(value(VERBOSE)))
184 		fflush(stdout);
185 #endif
186 	signal(SIGALRM, SIG_DFL);
187 	return (0);
188 }
189 
190 struct baud_msg {
191 	const char *msg;
192 	int baud;
193 } baud_msg[] = {
194 	{ "",		B300 },
195 	{ " 1200",	B1200 },
196 	{ " 2400",	B2400 },
197 	{ " 9600",	B9600 },
198 	{ " 9600/ARQ",	B9600 },
199 	{ 0,		0 }
200 };
201 
202 static int
203 cour_connect()
204 {
205 	char c;
206 	int nc, nl, n;
207 	char dialer_buf[64];
208 	struct baud_msg *bm;
209 	sig_t f;
210 
211 #if __GNUC__	/* XXX pacify gcc */
212 	(void)&nc;
213 	(void)&nl;
214 #endif
215 
216 	if (cour_swallow("\r\n") == 0)
217 		return (0);
218 	f = signal(SIGALRM, sigALRM);
219 again:
220 	memset(dialer_buf, 0, sizeof(dialer_buf));
221 	timeout = 0;
222 	for (nc = 0, nl = sizeof(dialer_buf) - 1 ; nl > 0 ; nc++, nl--) {
223 		if (setjmp(timeoutbuf))
224 			break;
225 		alarm(number(value(DIALTIMEOUT)));
226 		n = read(FD, &c, 1);
227 		alarm(0);
228 		if (n <= 0)
229 			break;
230 		c &= 0x7f;
231 		if (c == '\r') {
232 			if (cour_swallow("\n") == 0)
233 				break;
234 			if (!dialer_buf[0])
235 				goto again;
236 			if (strcmp(dialer_buf, "RINGING") == 0 &&
237 			    boolean(value(VERBOSE))) {
238 #ifdef DEBUG
239 				printf("%s\r\n", dialer_buf);
240 #endif
241 				goto again;
242 			}
243 			if (strncmp(dialer_buf, "CONNECT",
244 				    sizeof("CONNECT")-1) != 0)
245 				break;
246 			for (bm = baud_msg ; bm->msg ; bm++)
247 				if (strcmp(bm->msg,
248 				    dialer_buf+sizeof("CONNECT")-1) == 0) {
249 					struct termios	cntrl;
250 
251 					tcgetattr(FD, &cntrl);
252 					cfsetospeed(&cntrl, bm->baud);
253 					cfsetispeed(&cntrl, bm->baud);
254 					tcsetattr(FD, TCSAFLUSH, &cntrl);
255 					signal(SIGALRM, f);
256 #ifdef DEBUG
257 					if (boolean(value(VERBOSE)))
258 						printf("%s\r\n", dialer_buf);
259 #endif
260 					return (1);
261 				}
262 			break;
263 		}
264 		dialer_buf[nc] = c;
265 #ifdef notdef
266 		if (boolean(value(VERBOSE)))
267 			putchar(c);
268 #endif
269 	}
270 	printf("%s\r\n", dialer_buf);
271 	signal(SIGALRM, f);
272 	return (0);
273 }
274 
275 /*
276  * This convoluted piece of code attempts to get
277  * the courier in sync.
278  */
279 static int
280 coursync()
281 {
282 	int already = 0;
283 	int len;
284 	char buf[40];
285 
286 	while (already++ < MAXRETRY) {
287 		tcflush(FD, TCIOFLUSH);
288 		cour_write(FD, "\rAT Z\r", 6);	/* reset modem */
289 		memset(buf, 0, sizeof(buf));
290 		sleep(1);
291 		ioctl(FD, FIONREAD, &len);
292 		if (len) {
293 			len = read(FD, buf, sizeof(buf));
294 #ifdef DEBUG
295 			buf[len] = '\0';
296 			printf("coursync: (\"%s\")\n\r", buf);
297 #endif
298 			if (strchr(buf, '0') ||
299 			   (strchr(buf, 'O') && strchr(buf, 'K')))
300 				return(1);
301 		}
302 		/*
303 		 * If not strapped for DTR control,
304 		 * try to get command mode.
305 		 */
306 		sleep(1);
307 		cour_write(FD, "+++", 3);
308 		sleep(1);
309 		/*
310 		 * Toggle DTR to force anyone off that might have left
311 		 * the modem connected.
312 		 */
313 		ioctl(FD, TIOCCDTR, 0);
314 		sleep(1);
315 		ioctl(FD, TIOCSDTR, 0);
316 	}
317 	cour_write(FD, "\rAT Z\r", 6);
318 	return (0);
319 }
320 
321 static void
322 cour_write(fd, cp, n)
323 	int fd;
324 	const char *cp;
325 	int n;
326 {
327 
328 #ifdef notdef
329 	if (boolean(value(VERBOSE)))
330 		write(1, cp, n);
331 #endif
332 	tcdrain(fd);
333 	cour_nap();
334 	for ( ; n-- ; cp++) {
335 		write(fd, cp, 1);
336 		tcdrain(fd);
337 		cour_nap();
338 	}
339 }
340 
341 #ifdef DEBUG
342 static void
343 cour_verbose_read()
344 {
345 	int n = 0;
346 	char buf[BUFSIZ];
347 
348 	if (ioctl(FD, FIONREAD, &n) < 0)
349 		return;
350 	if (n <= 0)
351 		return;
352 	if (read(FD, buf, n) != n)
353 		return;
354 	write(1, buf, n);
355 }
356 #endif
357 
358 #define setsa(sa, a) \
359 	sa.sa_handler = a; sigemptyset(&sa.sa_mask); sa.sa_flags = 0
360 
361 static int napms = 50; /* Give the courier 50 milliseconds between characters */
362 
363 static int ringring;
364 
365 void
366 cour_nap()
367 {
368 
369 	struct itimerval itv, oitv;
370 	struct itimerval *itp = &itv;
371 	struct sigaction sa, osa;
372 	sigset_t sm, osm;
373 
374 	timerclear(&itp->it_interval);
375 	timerclear(&itp->it_value);
376 	if (setitimer(ITIMER_REAL, itp, &oitv) < 0)
377 		return;
378 
379 	sigemptyset(&sm);
380 	sigaddset(&sm, SIGALRM);
381 	(void)sigprocmask(SIG_BLOCK, &sm, &osm);
382 
383 	itp->it_value.tv_sec = napms/1000;
384 	itp->it_value.tv_usec = ((napms%1000)*1000);
385 
386 	setsa(sa, cour_napx);
387 	(void)sigaction(SIGALRM, &sa, &osa);
388 
389 	(void)setitimer(ITIMER_REAL, itp, NULL);
390 
391 	sm = osm;
392 	sigdelset(&sm, SIGALRM);
393 
394 	for (ringring = 0; !ringring; )
395 		sigsuspend(&sm);
396 
397 	(void)sigaction(SIGALRM, &osa, NULL);
398 	(void)setitimer(ITIMER_REAL, &oitv, NULL);
399 	(void)sigprocmask(SIG_SETMASK, &osm, NULL);
400 }
401 
402 static void
403 cour_napx(dummy)
404 	int dummy;
405 {
406 
407 	ringring = 1;
408 }
409