xref: /dragonfly/usr.bin/tip/unidialer.c (revision 279dd846)
1 /*
2  * Copyright (c) 1986, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#)unidialer.c	8.1 (Berkeley) 6/6/93
34  * $FreeBSD: src/usr.bin/tip/libacu/unidialer.c,v 1.7 1999/08/28 01:06:30 peter Exp $
35  */
36 
37 /*
38  * Generalized routines for calling up on a Hayes AT command set based modem.
39  * Control variables are pulled out of a modem caps-style database to
40  * configure the driver for a particular modem.
41  */
42 #include "tipconf.h"
43 #include "tip.h"
44 #include "pathnames.h"
45 
46 #include <sys/times.h>
47 #include <assert.h>
48 #include <err.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 
52 #include "acucommon.h"
53 #include "tod.h"
54 
55 /* #define DEBUG */
56 #define	MAXRETRY	5
57 
58 typedef enum
59 {
60 	mpt_notype, mpt_string, mpt_number, mpt_boolean
61 } modem_parm_type_t;
62 
63 typedef struct {
64 	modem_parm_type_t modem_parm_type;
65 	const char *name;
66 	union {
67 		char **string;
68 		unsigned int *number;
69 	} value;
70 	union {
71 		char *string;
72 		unsigned int number;
73 	} default_value;
74 } modem_parm_t;
75 
76 /*
77 	Configuration
78 */
79 static char modem_name [80];
80 static char *dial_command;
81 static char *hangup_command;
82 static char *echo_off_command;
83 static char *reset_command;
84 static char *init_string;
85 static char *escape_sequence;
86 static int hw_flow_control;
87 static int lock_baud;
88 static unsigned int intercharacter_delay;
89 static unsigned int intercommand_delay;
90 static unsigned int escape_guard_time;
91 static unsigned int reset_delay;
92 
93 static int unidialer_dialer(char *num, char *acu);
94 static void unidialer_disconnect(void);
95 static void unidialer_abort(void);
96 static int unidialer_connect(void);
97 static int unidialer_swallow(char *);
98 #ifdef DEBUG
99 static void unidialer_verbose_read (void);
100 #endif
101 static void unidialer_modem_cmd(int fd, CONST char *cmd);
102 static void unidialer_write(int fd, CONST char *cp, int n);
103 static void unidialer_write_str(int fd, CONST char *cp);
104 static void sigALRM(int);
105 static int unidialersync(void);
106 
107 static acu_t unidialer =
108 {
109 	modem_name,
110 	unidialer_dialer,
111 	unidialer_disconnect,
112 	unidialer_abort
113 };
114 
115 /*
116 	Table of parameters kept in modem database
117 */
118 modem_parm_t modem_parms [] = {
119 	{ mpt_string, "dial_command", { &dial_command }, { "ATDT%s\r" } },
120 	{ mpt_string, "hangup_command", { &hangup_command }, { "ATH\r", } },
121 	{ mpt_string, "echo_off_command", { &echo_off_command }, { "ATE0\r" } },
122 	{ mpt_string, "reset_command", { &reset_command }, { "ATZ\r" } },
123 	{ mpt_string, "init_string", { &init_string }, { "AT{ &F\r", } },
124 	{ mpt_string, "escape_sequence", { &escape_sequence }, { "+++" } },
125 	{ mpt_boolean, "hw_flow_control", { (char **)&hw_flow_control }, { NULL } },
126 	{ mpt_boolean, "lock_baud", { (char **)&lock_baud }, { NULL } },
127 	{ mpt_number, "intercharacter_delay", { (char **)&intercharacter_delay }, { (char *)50 } },
128 	{ mpt_number, "intercommand_delay", { (char **)&intercommand_delay }, { (char *)300 } },
129 	{ mpt_number, "escape_guard_time", { (char **)&escape_guard_time }, { (char *)300 } },
130 	{ mpt_number, "reset_delay", { (char **)&reset_delay }, { (char *)3000 } },
131 	{ mpt_notype, NULL, { NULL }, { NULL } }
132 };
133 
134 /*
135 	Global vars
136 */
137 static int timeout = 0;
138 static int connected = 0;
139 static jmp_buf timeoutbuf;
140 
141 #define cgetflag(f)	(cgetcap(bp, f, ':') != NULL)
142 
143 #ifdef DEBUG
144 
145 #define print_str(x) printf (#x " = %s\n", x)
146 #define print_num(x) printf (#x " = %d\n", x)
147 
148 void dumpmodemparms (char *modem)
149 {
150 		printf ("modem parms for %s\n", modem);
151 		print_str (dial_command);
152 		print_str (hangup_command);
153 		print_str (echo_off_command);
154 		print_str (reset_command);
155 		print_str (init_string);
156 		print_str (escape_sequence);
157 		print_num (lock_baud);
158 		print_num (intercharacter_delay);
159 		print_num (intercommand_delay);
160 		print_num (escape_guard_time);
161 		print_num (reset_delay);
162 		printf ("\n");
163 }
164 #endif
165 
166 static int getmodemparms (const char *modem)
167 {
168 	char *bp, *db_array [3], *modempath;
169 	int ndx, stat;
170 	modem_parm_t *mpp;
171 
172 	modempath = getenv ("MODEMS");
173 
174 	ndx = 0;
175 
176 	if (modempath != NULL)
177 		db_array [ndx++] = modempath;
178 
179 	db_array [ndx++] = _PATH_MODEMS;
180 	db_array [ndx] = NULL;
181 
182 	if ((stat = cgetent (&bp, db_array, (char *)modem)) < 0) {
183 		switch (stat) {
184 		case -1:
185 			warnx ("unknown modem %s", modem);
186 			break;
187 		case -2:
188 			warnx ("can't open modem description file");
189 			break;
190 		case -3:
191 			warnx ("possible reference loop in modem description file");
192 			break;
193 		}
194 		return 0;
195 	}
196 	for (mpp = modem_parms; mpp->name; mpp++)
197 	{
198 		switch (mpp->modem_parm_type)
199 		{
200 		case mpt_string:
201 			if (cgetstr (bp, (char *)mpp->name, mpp->value.string) == -1)
202 				*mpp->value.string = mpp->default_value.string;
203 			break;
204 		case mpt_number:
205 		{
206 			long l;
207 			if (cgetnum (bp, (char *)mpp->name, &l) == -1)
208 				*mpp->value.number = mpp->default_value.number;
209 			else
210 				*mpp->value.number = (unsigned int)l;
211 		}
212 			break;
213 		case mpt_boolean:
214 			*mpp->value.number = cgetflag ((char *)mpp->name);
215 			break;
216 		default:
217 			break;
218 		}
219 	}
220 	strncpy (modem_name, modem, sizeof (modem_name) - 1);
221 	modem_name [sizeof (modem_name) - 1] = '\0';
222 	return 1;
223 }
224 
225 /*
226 */
227 acu_t *
228 unidialer_getmodem(const char *modem_name)
229 {
230 	acu_t* rc = NULL;
231 	if (getmodemparms (modem_name))
232 		rc = &unidialer;
233 	return rc;
234 }
235 
236 static int
237 unidialer_modem_ready(void)
238 {
239 #ifdef TIOCMGET
240 	int state;
241 	ioctl (FD, TIOCMGET, &state);
242 	return (state & TIOCM_DSR) ? 1 : 0;
243 #else
244 	return (1);
245 #endif
246 }
247 
248 static int unidialer_waitfor_modem_ready (int ms)
249 {
250 #ifdef TIOCMGET
251 	int count;
252 	for (count = 0; count < ms; count += 100)
253 	{
254 		if (unidialer_modem_ready ())
255 		{
256 #ifdef DEBUG
257 			printf ("unidialer_waitfor_modem_ready: modem ready.\n");
258 #endif
259 			break;
260 		}
261 		acu_nap (100);
262 	}
263 	return (count < ms);
264 #else
265 	acu_nap (250);
266 	return (1);
267 #endif
268 }
269 
270 static void
271 unidialer_tty_clocal(int flag)
272 {
273 #if HAVE_TERMIOS
274 	struct termios t;
275 	tcgetattr (FD, &t);
276 	if (flag)
277 		t.c_cflag |= CLOCAL;
278 	else
279 		t.c_cflag &= ~CLOCAL;
280 	tcsetattr (FD, TCSANOW, &t);
281 #elif defined(TIOCMSET)
282 	int state;
283 	/*
284 		Don't have CLOCAL so raise CD in software to
285 		get the same effect.
286 	*/
287 	ioctl (FD, TIOCMGET, &state);
288 	if (flag)
289 		state |= TIOCM_CD;
290 	else
291 		state &= ~TIOCM_CD;
292 	ioctl (FD, TIOCMSET, &state);
293 #endif
294 }
295 
296 static int
297 unidialer_get_modem_response(char *buf, int bufsz, int response_timeout)
298 {
299 	sig_t f;
300 	char c, *p = buf, *lid = buf + bufsz - 1;
301 	int state;
302 
303 	assert (bufsz > 0);
304 
305 	f = signal (SIGALRM, sigALRM);
306 
307 	timeout = 0;
308 
309 	if (setjmp (timeoutbuf)) {
310 		signal (SIGALRM, f);
311 		*p = '\0';
312 #ifdef DEBUG
313 		printf ("get_response: timeout buf=%s, state=%d\n", buf, state);
314 #endif
315 		return (0);
316 	}
317 
318 	ualarm (response_timeout * 1000, 0);
319 
320 	state = 0;
321 
322 	while (1)
323 	{
324 		switch (state)
325 		{
326 			case 0:
327 				if (read (FD, &c, 1) == 1)
328 				{
329 					if (c == '\r')
330 					{
331 						++state;
332 					}
333 					else
334 					{
335 #ifdef DEBUG
336 						printf ("get_response: unexpected char %s.\n", ctrl (c));
337 #endif
338 					}
339 				}
340 				break;
341 
342 			case 1:
343 				if (read (FD, &c, 1) == 1)
344 				{
345 					if (c == '\n')
346 					{
347 #ifdef DEBUG
348 						printf ("get_response: <CRLF> encountered.\n");
349 #endif
350 						++state;
351 					}
352 					else
353 					{
354 							state = 0;
355 #ifdef DEBUG
356 							printf ("get_response: unexpected char %s.\n", ctrl (c));
357 #endif
358 					}
359 				}
360 				break;
361 
362 			case 2:
363 				if (read (FD, &c, 1) == 1)
364 				{
365 					if (c == '\r')
366 						++state;
367 					else if (c >= ' ' && p < lid)
368 						*p++ = c;
369 				}
370 				break;
371 
372 			case 3:
373 				if (read (FD, &c, 1) == 1)
374 				{
375 					if (c == '\n')
376 					{
377 						signal (SIGALRM, f);
378 						/* ualarm (0, 0); */
379 						alarm (0);
380 						*p = '\0';
381 #ifdef DEBUG
382 						printf ("get_response: %s\n", buf);
383 #endif
384 						return (1);
385 					}
386 					else
387 					{
388 						state = 0;
389 						p = buf;
390 					}
391 				}
392 				break;
393 		}
394 	}
395 }
396 
397 static int
398 unidialer_get_okay(int ms)
399 {
400 	int okay;
401 	char buf [BUFSIZ];
402 	okay = unidialer_get_modem_response (buf, sizeof (buf), ms) &&
403 		strcmp (buf, "OK") == 0;
404 	return okay;
405 }
406 
407 static int
408 unidialer_dialer(char *num, char *acu)
409 {
410 	char *cp;
411 	char dial_string [80];
412 #if ACULOG
413 	char line [80];
414 #endif
415 
416 #ifdef DEBUG
417 	dumpmodemparms (modem_name);
418 #endif
419 
420 	if (lock_baud) {
421 		int i;
422 		if ((i = speed(number(value(BAUDRATE)))) == 0)
423 			return 0;
424 		ttysetup (i);
425 	}
426 
427 	if (boolean(value(VERBOSE)))
428 		printf("Using \"%s\"\n", acu);
429 
430 	acu_hupcl ();
431 
432 	/*
433 	 * Get in synch.
434 	 */
435 	if (!unidialersync()) {
436 badsynch:
437 		printf("tip: can't synchronize with %s\n", modem_name);
438 #if ACULOG
439 		logent(value(HOST), num, modem_name, "can't synch up");
440 #endif
441 		return (0);
442 	}
443 
444 	unidialer_modem_cmd (FD, echo_off_command);	/* turn off echoing */
445 
446 	sleep(1);
447 
448 #ifdef DEBUG
449 	if (boolean(value(VERBOSE)))
450 		unidialer_verbose_read();
451 #endif
452 
453 	acu_flush (); /* flush any clutter */
454 
455 	unidialer_modem_cmd (FD, init_string);
456 
457 	if (!unidialer_get_okay (reset_delay))
458 		goto badsynch;
459 
460 	fflush (stdout);
461 
462 	for (cp = num; *cp; cp++)
463 		if (*cp == '=')
464 			*cp = ',';
465 
466 	(void) sprintf (dial_string, dial_command, num);
467 
468 	unidialer_modem_cmd (FD, dial_string);
469 
470 	connected = unidialer_connect ();
471 
472 	if (connected && hw_flow_control) {
473 		acu_hw_flow_control (hw_flow_control);
474 	}
475 
476 #if ACULOG
477 	if (timeout) {
478 		sprintf(line, "%d second dial timeout",
479 			number(value(DIALTIMEOUT)));
480 		logent(value(HOST), num, modem_name, line);
481 	}
482 #endif
483 
484 	if (timeout)
485 		unidialer_disconnect ();
486 
487 	return (connected);
488 }
489 
490 static void
491 unidialer_disconnect(void)
492 {
493 	int okay, retries;
494 
495 	acu_flush (); /* flush any clutter */
496 
497 	unidialer_tty_clocal (TRUE);
498 
499  	/* first hang up the modem*/
500 	ioctl (FD, TIOCCDTR, 0);
501 	acu_nap (250);
502 	ioctl (FD, TIOCSDTR, 0);
503 
504 	/*
505 	 * If AT&D2, then dropping DTR *should* just hangup the modem. But
506 	 * some modems reset anyway; also, the modem may be programmed to reset
507 	 * anyway with AT&D3. Play it safe and wait for the full reset time before
508 	 * proceeding.
509 	 */
510 	acu_nap (reset_delay);
511 
512 	if (!unidialer_waitfor_modem_ready (reset_delay))
513 	{
514 #ifdef DEBUG
515 			printf ("unidialer_disconnect: warning CTS low.\r\n");
516 #endif
517 	}
518 
519 	/*
520 	 * If not strapped for DTR control, try to get command mode.
521 	 */
522 	for (retries = okay = 0; retries < MAXRETRY && !okay; retries++)
523 	{
524 		int timeout_value;
525 		/* flush any clutter */
526 		if (!acu_flush ())
527 		{
528 #ifdef DEBUG
529 			printf ("unidialer_disconnect: warning flush failed.\r\n");
530 #endif
531 		}
532 		timeout_value = escape_guard_time;
533 		timeout_value += (timeout_value * retries / MAXRETRY);
534 		acu_nap (timeout_value);
535 		acu_flush (); /* flush any clutter */
536 		unidialer_modem_cmd (FD, escape_sequence);
537 		acu_nap (timeout_value);
538 		unidialer_modem_cmd (FD, hangup_command);
539 		okay = unidialer_get_okay (reset_delay);
540 	}
541 	if (!okay)
542 	{
543 #if ACULOG
544 		logent(value(HOST), "", modem_name, "can't hang up modem");
545 #endif
546 		if (boolean(value(VERBOSE)))
547 			printf("hang up failed\n");
548 	}
549 	(void) acu_flush ();
550 	close (FD);
551 }
552 
553 static void
554 unidialer_abort(void)
555 {
556 	unidialer_write_str (FD, "\r");	/* send anything to abort the call */
557 	unidialer_disconnect ();
558 }
559 
560 static void
561 sigALRM(int signo)
562 {
563 	(void) printf("\07timeout waiting for reply\n");
564 	timeout = 1;
565 	longjmp(timeoutbuf, 1);
566 }
567 
568 static int unidialer_swallow (char *match)
569 {
570 	sig_t f;
571 	char c;
572 
573 	f = signal(SIGALRM, sigALRM);
574 
575 	timeout = 0;
576 
577 	if (setjmp(timeoutbuf)) {
578 		signal(SIGALRM, f);
579 		return (0);
580 	}
581 
582 	alarm(number(value(DIALTIMEOUT)));
583 
584 	do {
585 		if (*match =='\0') {
586 			signal(SIGALRM, f);
587 			alarm (0);
588 			return (1);
589 		}
590 		do {
591 			read (FD, &c, 1);
592 		} while (c == '\0');
593 		c &= 0177;
594 #ifdef DEBUG
595 		if (boolean(value(VERBOSE)))
596 		{
597 			/* putchar(c); */
598 			printf (ctrl (c));
599 		}
600 #endif
601 	} while (c == *match++);
602 	signal(SIGALRM, SIG_DFL);
603 	alarm(0);
604 #ifdef DEBUG
605 	if (boolean(value(VERBOSE)))
606 		fflush (stdout);
607 #endif
608 	return (0);
609 }
610 
611 static struct baud_msg {
612 	char *msg;
613 	int baud;
614 } baud_msg[] = {
615 	{ "",		B300 },
616 	{ " 1200",	B1200 },
617 	{ " 2400",	B2400 },
618 	{ " 9600",	B9600 },
619 	{ " 9600/ARQ",	B9600 },
620 	{ 0,		0 },
621 };
622 
623 static int
624 unidialer_connect(void)
625 {
626 	char c;
627 	int nc, nl, n;
628 	char dialer_buf[64];
629 	struct baud_msg *bm;
630 	sig_t f;
631 
632 	if (unidialer_swallow("\r\n") == 0)
633 		return (0);
634 	f = signal(SIGALRM, sigALRM);
635 again:
636 	nc = 0; nl = sizeof(dialer_buf)-1;
637 	bzero(dialer_buf, sizeof(dialer_buf));
638 	timeout = 0;
639 	for (nc = 0, nl = sizeof(dialer_buf)-1 ; nl > 0 ; nc++, nl--) {
640 		if (setjmp(timeoutbuf))
641 			break;
642 		alarm(number(value(DIALTIMEOUT)));
643 		n = read(FD, &c, 1);
644 		alarm(0);
645 		if (n <= 0)
646 			break;
647 		c &= 0x7f;
648 		if (c == '\r') {
649 			if (unidialer_swallow("\n") == 0)
650 				break;
651 			if (!dialer_buf[0])
652 				goto again;
653 			if (strcmp(dialer_buf, "RINGING") == 0 &&
654 			    boolean(value(VERBOSE))) {
655 #ifdef DEBUG
656 				printf("%s\r\n", dialer_buf);
657 #endif
658 				goto again;
659 			}
660 			if (strncmp(dialer_buf, "CONNECT",
661 				    sizeof("CONNECT")-1) != 0)
662 				break;
663 			if (lock_baud) {
664 				signal(SIGALRM, f);
665 #ifdef DEBUG
666 				if (boolean(value(VERBOSE)))
667 					printf("%s\r\n", dialer_buf);
668 #endif
669 				return (1);
670 			}
671 			for (bm = baud_msg ; bm->msg ; bm++)
672 				if (strcmp(bm->msg, dialer_buf+sizeof("CONNECT")-1) == 0) {
673 					if (!acu_setspeed (bm->baud))
674 						goto error;
675 					signal(SIGALRM, f);
676 #ifdef DEBUG
677 					if (boolean(value(VERBOSE)))
678 						printf("%s\r\n", dialer_buf);
679 #endif
680 					return (1);
681 				}
682 			break;
683 		}
684 		dialer_buf[nc] = c;
685 	}
686 	printf("%s\r\n", dialer_buf);
687 error:
688 	signal(SIGALRM, f);
689 	return (0);
690 }
691 
692 /*
693  * This convoluted piece of code attempts to get
694  * the unidialer in sync.
695  */
696 static int
697 unidialersync(void)
698 {
699 	int already = 0;
700 	int len;
701 	char buf[40];
702 
703 	while (already++ < MAXRETRY) {
704 		acu_nap (intercommand_delay);
705 		acu_flush (); /* flush any clutter */
706 		unidialer_write_str (FD, reset_command); /* reset modem */
707 		bzero(buf, sizeof(buf));
708 		acu_nap (reset_delay);
709 		ioctl (FD, FIONREAD, &len);
710 		if (len) {
711 			len = read(FD, buf, sizeof(buf));
712 #ifdef DEBUG
713 			buf [len] = '\0';
714 			printf("unidialersync (%s): (\"%s\")\n\r", modem_name, buf);
715 #endif
716 			if (index(buf, '0') ||
717 		   	   (index(buf, 'O') && index(buf, 'K')))
718 				return(1);
719 		}
720 		/*
721 		 * If not strapped for DTR control,
722 		 * try to get command mode.
723 		 */
724 		acu_nap (escape_guard_time);
725 		unidialer_write_str (FD, escape_sequence);
726 		acu_nap (escape_guard_time);
727 		unidialer_write_str (FD, hangup_command);
728 		/*
729 		 * Toggle DTR to force anyone off that might have left
730 		 * the modem connected.
731 		 */
732 		acu_nap (escape_guard_time);
733 		ioctl (FD, TIOCCDTR, 0);
734 		acu_nap (1000);
735 		ioctl (FD, TIOCSDTR, 0);
736 	}
737 	acu_nap (intercommand_delay);
738 	unidialer_write_str (FD, reset_command);
739 	return (0);
740 }
741 
742 /*
743 	Send commands to modem; impose delay between commands.
744 */
745 static void unidialer_modem_cmd (int fd, const char *cmd)
746 {
747 	static struct timeval oldt = { 0, 0 };
748 	struct timeval newt;
749 	tod_gettime (&newt);
750 	if (tod_lt (&newt, &oldt))
751 	{
752 		unsigned int naptime;
753 		tod_subfrom (&oldt, newt);
754 		naptime = oldt.tv_sec * 1000 + oldt.tv_usec / 1000;
755 		if (naptime > intercommand_delay)
756 		{
757 #ifdef DEBUG
758 		printf ("unidialer_modem_cmd: suspicious naptime (%u ms)\r\n", naptime);
759 #endif
760 			naptime = intercommand_delay;
761 		}
762 #ifdef DEBUG
763 		printf ("unidialer_modem_cmd: delaying %u ms\r\n", naptime);
764 #endif
765 		acu_nap (naptime);
766 	}
767 	unidialer_write_str (fd, cmd);
768 	tod_gettime (&oldt);
769 	newt.tv_sec = 0;
770 	newt.tv_usec = intercommand_delay;
771 	tod_addto (&oldt, &newt);
772 }
773 
774 static void unidialer_write_str (int fd, const char *cp)
775 {
776 #ifdef DEBUG
777 	printf ("unidialer (%s): sending %s\n", modem_name, cp);
778 #endif
779 	unidialer_write (fd, cp, strlen (cp));
780 }
781 
782 static void unidialer_write (int fd, const char *cp, int n)
783 {
784 	acu_nap (intercharacter_delay);
785 	for ( ; n-- ; cp++) {
786 		write (fd, cp, 1);
787 		acu_nap (intercharacter_delay);
788 	}
789 }
790 
791 #ifdef DEBUG
792 static void unidialer_verbose_read(void)
793 {
794 	int n = 0;
795 	char buf[BUFSIZ];
796 
797 	if (ioctl(FD, FIONREAD, &n) < 0)
798 		return;
799 	if (n <= 0)
800 		return;
801 	if (read(FD, buf, n) != n)
802 		return;
803 	write(1, buf, n);
804 }
805 #endif
806 
807 /* end of unidialer.c */
808