Subject: Patches to com.c (NetBSD 1.0) to implement callout devices.
To: None <port-i386@NetBSD.ORG>
From: Brian Buhrow <buhrow@cats.ucsc.edu>
List: port-i386
Date: 01/23/1995 10:23:58
	Hello fellow netbsd users.  I have a set of patches for the i386 com
driver for NetBSD 1.0 which implement callout devices.  If you want these,
mail me at:
buhrow@nfbcal.org.

Here is the readme.

	Here are the diffs necessary to use callout devices on serial ports under
NetBSD 1.0.  I have  had friends testing these diffs for over a month, and
they have been quite pleased with them.  I run an earlier encarnation of
these patches on my production system and they have been quite reliable.
That said, however, be aware that your mileage  may vary.  

HOW TO USE:
1.  Apply the enclosed diff file to /usr/src/sys/arch/i386/isa/com.c as it
comes from the 1.0 distribution set.  Use the patch program.

2.  Edit your kernel configuration file to include:
options "COM_BIDIR"

3.  For each com port that you want to have the special bidirectional
capability, edit the line so that it includes the words: "flags 2"
For example, 
device		com0	at isa? port "IO_COM1" irq 4
becomes:
device		com0	at isa? port "IO_COM1" irq 4 flags 2

	If you are using an AST multiport board, or some other board which uses
interrupt multiplexing, then you may need to specify a value of 3 for the
flags so that you disable interrupts for each uart, and take advantage of
the multiplexed interrupt vector.  

3.  Re config and build your kernel.  

4.  Saving a copy of your known working kernel in the root partition of
your
system, install your newly built kernel.  Reboot.

5.  Running on your new kernel, become root and cd /dev.
For those serial ports where you want bidirectional capabilities, use mknod
to create the callout device.  For example, to create the callout device
for com0 (tty00), use the following syntax:
mknod cu00 c 8 128

Note that the minor device number of the callout device is 128 + the minor
device number of the dial-in device.  Thus, tty01 becomes 129, tty02 becomes
130, etc.  

6.  Edit your /etc/ttys file to turn on getty on those ports where you want
people to be able to dial in.  

I use entries of the form:
tty00	"/usr/libexec/getty D19200"	unknown on rtscts

	This gives me full hardware flow control on my modem for the 
dial in line and takes advantage of the auto-baud code in getty.  Actually,
this code is very suspect, but it seems to work for me.

7.  To use the callout device, fire up kermit and set line to the name of
the callout device you created in step 5 above.  Note that you may need to
set the baud and flow control method from within kermit.

NOTES:
	This is an interim version of this patch.  That is, I know of some 
limitations inherent in this patch, but they seemed minor enough to allow
the patch to go out the door.  The largest limitation was pointed out  by
Bakul Sha (bakul@netcom.com) who pointed out that selects on the callout
device would not work under this implementation.  Also, there is a known
bug whereby if getty is waiting for carrier detect on a modem with the
bidirectional port turned on, and a process opens the call-in device in
non-blocking mode, the callout device becomes busy until getty dies and
starts a new waiting procedure.  This can happen when someone dials in on
the line or if the power on the modem is cycled.  Finally, the next 
implementation will try to use memory more conservatively.
	Although I believe this patch will cause no other conflicts in the 
NetBSD 1.0 system, you should save an original of the com.c driver in case
thingsgo badly.

	Please send comments, suggestions, bug fixes, etc. to:
buhrow@nfbcal.org

35c35
<  *	$Id: com.c,v 1.1 1994/12/16 22:04:42 buhrow Exp $
---
>  *	$Id: com.c,v 1.2 1994/12/18 19:39:25 buhrow Exp $
41a42,56
> 
>  /*
>  *Callout devices added by Brian Buhrow on December 18, 1994.
>  *These additions are based upon patches to 0.9 as written by
>  *Hannes-Christopf Deeken <hannes@hotb.sub.org>
>  *and modified by me to run on the ast card under 0.9a.
>  *
>  *Use options "COM_BIDIR" to enable the callout code.  
>  *Use flags = 2 to enable callout on each port in the config file.  
>  *NOTE: If you use the ast card or another card which does interrupt
>  *multiplexing, and you want callout capabilities, use flags = 3.
>  *Otherwise, follow the instructions given in the faq for installing
>  *multiport cards.
>  */
> 
85a101,117
> #ifdef COM_BIDIR /**/
> /* how long to hold DTR down on close (* 1/HZ) */
> #ifndef COM_DTRWAIT
> #define COM_DTRWAIT 50
> #endif /* COM_DTRWAIT */
> #define COM_HW_BIDIR 0x02 /*bidirectional flag in config*/
> struct com_bidir_info {
>       int     bidir;      /* is this unit bidirectional? */
>       int     active;     /* is the port active _at all_? */
>       int     active_in;  /* is the incoming port in use? */
>       int     active_out; /* is the outgoing port in use? */
>       int     fake_dcd;   /* should we fake DCD for now? */
> };
>  
> struct com_bidir_info com_bdi[NCOM];
> #endif /* COM_BIDIR */
> 
114a147,153
> #ifdef COM_BIDIR
> #define	COM_UNITMASK	0x7f
> #define	COM_CALLOUTMASK	0x80
> 
> #define	COMUNIT(x)		(minor(x) & COM_UNITMASK)
> #define	CALLOUT(x)	(minor(x) & COM_CALLOUTMASK)
> #else
115a155
> #endif /* COM_BIDIR */
187a228,230
> #ifdef COM_BIDIR
> 	int unit = sc->sc_dev.dv_unit & COM_UNITMASK;
> #endif /*COM_BIDIR*/
249a293,302
> #ifdef COM_BIDIR
> 	/*If the bidirectional port is enabled on this device, then set up
> 	the appropriate info*/
> 	com_bdi[unit].bidir = cf->cf_flags & COM_HW_BIDIR; /*set bidir flag*/
> 	com_bdi[unit].active = 0;
> 	com_bdi[unit].active_out = com_bdi[unit].active_in = 0;
> 	com_bdi[unit].fake_dcd = 0;
> 	if (com_bdi[unit].bidir > 0)
> 		printf("com%d: bidirectional\n",unit);
> #endif /*COM_BIDIR*/
263a317,319
> #ifdef COM_BIDIR
> 	int callout = CALLOUT(dev);
> #endif /*COM_BIDIR*/
266a323,326
> #ifdef COM_BIDIR
> 	/*if its a call out unit and bidir is not configured, don't proceed.*/
> 	if (callout && !(com_bdi[unit].bidir)) return(ENXIO);
> #endif /*COM_BIDIR*/
270a331,410
> 	iobase = sc->sc_iobase;
> 	sc->sc_msr = inb(iobase + com_msr);
> #ifdef COM_BIDIR
> 
>  bidir_open_top:
> 
> 	/* if it's bidirectional, we've gotta deal with it... */
> 	if (com_bdi[unit].bidir) {
> 		if (callout) {
> 			if (com_bdi[unit].active_in) {
> 			    /* it's busy. die */
> 			    (void) spl0();
> 			    return (EBUSY);
> 			} else {
> 			    /* it's ours.  lock it down, and set it up */
> 			    com_bdi[unit].active_out = 1;
> 			    com_bdi[unit].fake_dcd = 1;
> 			}
> 		} else {
> 			if (com_bdi[unit].active_out) {
> 				/* it's busy, outgoing.  wait, if possible */
> 				if (flag & O_NONBLOCK) {
> 				    /* can't wait; bail */
> 				    (void) spl0();
> 				    return (EBUSY);
> 				} else {
> 				    /* wait for it... */
> 				    error = tsleep((caddr_t)&com_bdi[unit].active_out,
> 						   TTIPRI|PCATCH,
> 						   "comoth",
> 						   0);
> 				    /* if there was an error, take off. */
> 				    if (error != 0) {
> 					(void) spl0();
> 					return (error);
> 				    }
> 				    /* else take it from the top */
> 				    goto bidir_open_top;
> 				}
> 			} else if (sc->sc_msr  & MSR_DCD) {
> 				/* there's a carrier on the line; we win */
> 				com_bdi[unit].active_in = 1;
> 				com_bdi[unit].fake_dcd = 0;
> 			} else {
> 				/* there is no carrier on the line */
> 				if (flag & O_NONBLOCK) {
> 				    /* can't wait; let it open */
> 				    com_bdi[unit].active_in = 1;
> 				    com_bdi[unit].fake_dcd = 0;
> 				} else {
> 				    /* put DTR & RTS up */
> 				    bis(iobase + com_mcr, MCR_DTR|MCR_RTS|sc->sc_mcr);
> 				    outb(iobase+com_ier,
> 						inb(iobase+com_ier) | IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC);
> 
> 				    /* wait for it... */
> 				    error = tsleep((caddr_t)&com_bdi[unit].active_in,
> 						   TTIPRI|PCATCH,
> 						   "comdcd",
> 						   0);
> 						   if (!error) com_bdi[unit].active = 1;
> 
> 				    /* if not active, turn DTR & RTS off */
> 				    if (!com_bdi[unit].active)
> 					bic(iobase + com_mcr, MCR_DTR|MCR_RTS);
> 
> 				    /* if there was an error, take off. */
> 				    if (error != 0) {
> 					(void) spl0();
> 					return (error);
> 				    }
> 				    /* else take it from the top */
> 				    goto bidir_open_top;
> 				}
> 			}
> 		}
> 	}
> 
> 	com_bdi[unit].active = 1;
> #endif /* COM_BIDIR */
298d437
< 		iobase = sc->sc_iobase;
316a456,458
> #ifdef COM_BIDIR
> 		    com_bdi[unit].fake_dcd ||
> #endif /*COM_BIDIR*/
327c469,473
< 	if ((flag & O_NONBLOCK) == 0)
---
> #ifdef COM_BIDIR
> 	if (!com_bdi[unit].bidir && !(flag & O_NONBLOCK))
> #else /*COM_BIDIR*/
> 	if (!(flag & O_NONBLOCK))
> #endif /*COM_BIDIR*/
340a487,490
> #ifdef COM_BIDIR
> 	/* wakeup sleepers */
> 	wakeup((caddr_t) &com_bdi[unit].active_in);
> #endif /* COM_BIDIR */
343a494,502
> 
> #ifdef COM_BIDIR
> 
> void comclose_wakeup(arg)
> 	caddr_t arg;
> {
> 	wakeup(arg);
> }
> #endif /*COM_BIDIR*/
354a514
> 	int error;
375a536,550
> #ifdef COM_BIDIR
> 	do {
> 		timeout(comclose_wakeup, (caddr_t)&com_bdi[unit].active,
> 			COM_DTRWAIT);
> 		error = tsleep((caddr_t)&com_bdi[unit].active, TTIPRI|PCATCH,
> 			       "comclose", 0);
> 	} while (error == ERESTART);
> 	outb(iobase + com_mcr, 0);
> 	com_bdi[unit].active = com_bdi[unit].active_in =
> 	    com_bdi[unit].active_out = 0;
> 	com_bdi[unit].fake_dcd = 0;
> 
>         /* wakeup sleepers who are waiting for out to finish */
> 	wakeup((caddr_t) &com_bdi[unit].active_out);
> #endif /*COM_BIDIR*/
468,469c643,647
< 		if (m & MSR_DCD)
< 			bits |= TIOCM_CD;
---
> 		if ((m & MSR_DCD)
> #ifdef COM_BIDIR
> 			|| com_bdi[unit].fake_dcd
> #endif /* COM_BIDIR */
> 			) bits |= TIOCM_CD;
729a908
> 	int unit = sc->sc_dev.dv_unit & COM_UNITMASK;
735,736c914,925
< 	if (delta & MSR_DCD && (sc->sc_swflags & COM_SW_SOFTCAR) == 0) {
< 		if (msr & MSR_DCD)
---
> 	if ((delta & MSR_DCD) && ((sc->sc_swflags & COM_SW_SOFTCAR) == 0)) { /**/
> 		if (msr & MSR_DCD) {
> #ifdef COM_BIDIR
> 			if ((*linesw[tp->t_line].l_modem)(tp, 1))
> 			    /* tty layer has ack'd carrier up, so stop faking */
> 			    com_bdi[unit].fake_dcd = 0;
> 
> 			/* wakeup sleepers on active_in, who
> 			 * are waiting for DCD
> 			 */
> 			wakeup((caddr_t) &com_bdi[unit].active_in);
> #else
737a927,928
> #endif /*COM_BIDIR*/
> 		}