[pe.cust.wanted] How to open a modem which isn't asserting carrier-detect?

dave@lsuc.UUCP (David Sherman) (01/30/85)

Scenario: a Perkin-Elmer 3220 running v7, with source. Various
lines to modems and similar DCE devices. Normally, init waits on
an open(2) call; when a call comes in to the modem, the modem
raises CXR (carrier detect), the open succeeds, and init starts
up a getty. Fine.

Occasionally, I want an open to succeed whether or not CXR is
asserted by the modem. For example, I want to call out with cu(1).
I am *not* interested in solutions which involve changing a setting
on the modem, or jumpers within the cable. This change has to
be possible from the software, on any line at any time.

Looking at the code in /usr/sys/dev/vdu.c, I find:

vduopen(dev)
{
 ...
	vduenab(tp);
 ...
	while (!(tp->t_state&CARR_ON))
		sleep(&tp->t_rawq, TTIPRI);
 ...
}

vduenab(atp)
{
 ...
	stat = ss(radd);
	if ((stat&CARR_OFF) == 0)
		tp->t_state |= CARR_ON;
 ...
}

So it looks like the change is fairly simple: given an indication
that we don't care about the CARR_OFF (hardware) bit, vduenab can
simply turn on the CARR_ON indicator in t_state and we're off to the races.

Question (for those who are still with me): how do I get that
indication to the kernel - that is, that I want the open to work
whether or not the carrier is asserted? I can't use an ioctl,
because you have to get the device open first before you can ioctl.
Do I have to go through the hassle and overhead of another, almost
identical, device driver, which points to the same address?
Is there a better way?

All ideas greatly appreciated.

Dave Sherman
The Law Society of Upper Canada
Toronto
-- 
{utzoo pesnta nrcaero utcs}!lsuc!dave
{allegra decvax ihnp4 linus}!utcsrgv!lsuc!dave

jer@peora.UUCP (J. Eric Roskos) (01/31/85)

> Scenario: a Perkin-Elmer 3220 running v7, with source. Various
> lines to modems and similar DCE devices. Normally, init waits on
> an open(2) call; when a call comes in to the modem, the modem
> raises CXR (carrier detect), the open succeeds, and init starts
> up a getty. Fine.
> 
> Occasionally, I want an open to succeed whether or not CXR is
> asserted by the modem. For example, I want to call out with cu(1).
> I am *not* interested in solutions which involve changing a setting
> on the modem, or jumpers within the cable. This change has to
> be possible from the software, on any line at any time.


Fortunately, I made this change to our system about a month ago, as part
of an implementation of a modification to UUCP.

The change is a little more complicated than what you've described, although
not unduly so.  In addition to the problems you listed, if you just allow
the open to proceed with no carrier, other parts of the tty driver will
throw away characters due to the no-carrier condition.

The best way to do this is to define another device which allows opening
with no carrier.  It doesn't require any duplication of code, and leads to
a really straightforward implementation of the original UUCP ACU dialing
code (the original UUCP ACU handler works with no modifications), besides.

The following diffs show the changes.  The files tty.c, vdu.c, and mkconf.c
must be changed: tty.c and vdu.c to change logic in the handlers, mkconf.c
to add the new device (which comes out as device 25 on the PE Edition VII
implementation as distributed, I believe).

The changes work as follows.  When you open a tty using the new device id,
the flag DKCALL is set in the vdu structure to indicate this fact.  Then,
throughout the driver, this flag is checked (in disjunction with CARR_ON)
wherever carrier detection is done.  (I used DKCALL simply because it was
not used by the vdu driver, and there were no unassigned bits available
to use.  You might want to rename DKCALL via a #define to improve the
readability of the program.)

A drawback of this approach (which you must be careful about) is that there
are 2 device ids for each device, and the device id used in the LAST
SUCCESSFUL open done to the device determines whether carrier is checked
for or not.  In short, you should have only one of the 2 devices open at
any one time.

A very strong advantage of this approach is this:  you can do an open() call
on the original (carrier-detecting) device, which will be suspended because
there is no carrier present; then do an open() call on the new (carrier-
ignoring) device, which will succeed, send a dial command (if it's a
smart-modem like a Hayes(tm) or USRobotics(tm)), and close the new
device.  When the dial completes, the original open will proceed.  Because
of the structure of the open code, this will all work correctly... and it
is exactly how the original ACU code in UUCP works.  For this reason, you
can treat the new device as a call-dialing unit, if you want.

Following are the required changes.  The files with the suffix .noacu are
the original files; the ones without the suffix are the revised files.

In addition, you must add the line

acu

to your configuration file to be processed by mkconf.  It uses the device
addresses & other parameters supplied by the normal tty definition, and
thus neither requires nor accepts any of its own.

You also must use mknod to create an inode with the proper major and minor
device numbers in the /dev directory.  Check the output from mkconf to
determine the major device number (probably 25); the minor device number
is the same as for the original tty device for that line.


Incidentally, this code also contains a correction for a bug in the original
tty handlers, in which device address arrays were improperly indexed using
the entire device id, rather than just the minor device id.

----------

*** mkconf.c.noacu	Wed Jan 30 16:51:09 1985
--- mkconf.c	Wed Jan 30 16:50:58 1985
***************
*** 639,644
  	}
  	,
  
  	/* Null name marks end of table */
  
  	{	

--- 639,663 -----
  	}
  	,
  
+ 	/* Comms Mux, PALS or PASLA with open on no-carrier (for ACUs) */
+ 
+ 	{
+ 		"acu", CDEV+CONSDEV, MAXVDU, 1,
+ 		{ 
+ 			0 		}
+ 		,
+ 		{ 
+ 			"acuopen", "vduclose", "vduread", "vduwrite", "vduioctl",
+ 			"vdustop", "vdu" 		}
+ 		,
+ 		{ 
+ 			"vdurint", "vduxint" 		}
+ 		,
+ 		{
+ 			0               }
+ 		,
+ 	}
+ 	,
  	/* Null name marks end of table */
  
  	{	
***************
*** 1103,1108
  					continue;
  				}
  				if (!strcmp(dp->d_name,"pts"))
  					continue;
  				printf("struct tty      %s[%d];\n",
  				dp->d_csw[6], dp->d_ndev);

--- 1122,1129 -----
  					continue;
  				}
  				if (!strcmp(dp->d_name,"pts"))
+ 					continue;
+ 				if (!strcmp(dp->d_name,"acu"))
  					continue;
  				printf("struct tty      %s[%d];\n",
  				dp->d_csw[6], dp->d_ndev);
*** tty.c.noacu	Wed Jan 30 16:42:30 1985
--- tty.c	Wed Jan 30 16:41:48 1985
***************
*** 413,419
  	spl5();
  	while ((tp->t_flags&(RAW|CBREAK))==0 && tp->t_delct==0
  	    || (tp->t_flags&(RAW|CBREAK))!=0 && tp->t_rawq.c_cc==0) {
! 		if ((tp->t_state&CARR_ON)==0 || tp->t_chan!=NULL) {
  			return(0);
  		}
  		sleep((caddr_t)&tp->t_rawq, TTIPRI);

--- 452,458 -----
  	spl5();
  	while ((tp->t_flags&(RAW|CBREAK))==0 && tp->t_delct==0
  	    || (tp->t_flags&(RAW|CBREAK))!=0 && tp->t_rawq.c_cc==0) {
! 		if ((tp->t_state&(CARR_ON|DKCALL))==0 || tp->t_chan!=NULL) {
  			return(0);
  		}
  		sleep((caddr_t)&tp->t_rawq, TTIPRI);
***************
*** 768,774
  register struct tty *tp;
  {
  
! 	if ((tp->t_state&CARR_ON)==0)
  		return(0);
  	if (tp->t_canq.c_cc || canon(tp))
  		while (tp->t_canq.c_cc && passc(getc(&tp->t_canq))>=0)

--- 807,813 -----
  register struct tty *tp;
  {
  
! 	if ((tp->t_state&(CARR_ON|DKCALL))==0)
  		return(0);
  	if (tp->t_canq.c_cc || canon(tp))
  		while (tp->t_canq.c_cc && passc(getc(&tp->t_canq))>=0)
***************
*** 792,798
  	register c;
  #endif
  
! 	if ((tp->t_state&CARR_ON)==0)
  		return(NULL);
  	while (u.u_count) {
  #ifdef	CGL_CBLK

--- 831,837 -----
  	register c;
  #endif
  
! 	if ((tp->t_state&(CARR_ON|DKCALL))==0)
  		return(NULL);
  	while (u.u_count) {
  #ifdef	CGL_CBLK
*** vdu.c.noacu	Wed Jan 30 16:42:07 1985
--- vdu.c	Wed Jan 30 16:41:58 1985
***************
*** 2,7
   *		Vdu driver -- local terminal on PALS
   *
   *	- uses Autodriver Channel for output
   *
   */
  

--- 2,9 -----
   *		Vdu driver -- local terminal on PALS
   *
   *	- uses Autodriver Channel for output
+  *      - with "acu" device open routines for opening Hayes-compatible
+  *        autodial modems when no carrier detect is present JER 1/4/85
   *
   */
  
***************
*** 77,82
   */
  vduopen(dev)
  {
  	register struct tty *tp;
  
  	if ((minor(dev) >= nvdu) || ( minor(dev) >= MAXVDU )) {

--- 79,94 -----
   */
  vduopen(dev)
  {
+ 	xvduopen(dev,1);
+ }
+ 
+ acuopen(dev)
+ {
+ 	xvduopen(dev,0);
+ }
+ 
+ xvduopen(dev,carr)
+ {
  	register struct tty *tp;
  
  	if ((minor(dev) >= nvdu) || ( minor(dev) >= MAXVDU )) {
***************
*** 96,103
  		vduenab(tp);
  	}
  	spl4();
! 	while (!(tp->t_state&CARR_ON))
! 		sleep(&tp->t_rawq, TTIPRI);
  	spl0();
  	if ((tp->t_state&XCLUDE) && u.u_uid != 0) {
  		u.u_error = EBUSY;

--- 108,120 -----
  		vduenab(tp);
  	}
  	spl4();
! 	if (carr) {
! 		while (!(tp->t_state&CARR_ON))
! 			sleep(&tp->t_rawq, TTIPRI);
! 		tp->t_state &= ~DKCALL; /* JER flag non-dial-out */
! 	}
! 	else
! 		tp->t_state |= DKCALL; /* JER flag dial-out mode */
  	spl0();
  	if ((tp->t_state&XCLUDE) && u.u_uid != 0) {
  		u.u_error = EBUSY;
***************
*** 174,180
  struct tty *atp;
  {
  	register struct tty *tp;
! 	register c, waddr;
  	register struct ccb *ccb;
  
  	tp = atp;

--- 191,197 -----
  struct tty *atp;
  {
  	register struct tty *tp;
! 	register c, waddr,dkc;
  	register struct ccb *ccb;
  
  	tp = atp;
***************
*** 178,184
  	register struct ccb *ccb;
  
  	tp = atp;
! 	waddr = vduaddr[tp->t_dev] + 1;
  trace(1<<16, "vdustart", waddr);
  	if ((tp->t_state&(TIMEOUT|TTSTOP|BUSY|CARR_ON)) != CARR_ON
  	    || (c = getc(&tp->t_outq)) < 0)

--- 195,201 -----
  	register struct ccb *ccb;
  
  	tp = atp;
! 	waddr = vduaddr[minor(tp->t_dev)] + 1;
  trace(1<<16, "vdustart", waddr);
  	dkc = (tp->t_state&DKCALL)? 0: CARR_ON; /* JER CARR ck only if not acu */
  	if ((tp->t_state&(TIMEOUT|TTSTOP|BUSY|dkc)) != dkc
***************
*** 180,186
  	tp = atp;
  	waddr = vduaddr[tp->t_dev] + 1;
  trace(1<<16, "vdustart", waddr);
! 	if ((tp->t_state&(TIMEOUT|TTSTOP|BUSY|CARR_ON)) != CARR_ON
  	    || (c = getc(&tp->t_outq)) < 0)
  		return;
  	if (c > 0177 && (tp->t_flags&RAW) == 0) {

--- 197,204 -----
  	tp = atp;
  	waddr = vduaddr[minor(tp->t_dev)] + 1;
  trace(1<<16, "vdustart", waddr);
! 	dkc = (tp->t_state&DKCALL)? 0: CARR_ON; /* JER CARR ck only if not acu */
! 	if ((tp->t_state&(TIMEOUT|TTSTOP|BUSY|dkc)) != dkc
  	    || (c = getc(&tp->t_outq)) < 0)
  		return;
  	if (c > 0177 && (tp->t_flags&RAW) == 0) {
***************
*** 240,247
  	register struct tty *tp;
  	register char c;
  
! 	tp = &vdu[dev];
! 	raddr = vduaddr[dev];
  
  	if (!(tp->t_state & ISOPEN))
  		return;

--- 258,265 -----
  	register struct tty *tp;
  	register char c;
  
! 	tp = &vdu[minor(dev)];
! 	raddr = vduaddr[minor(dev)];
  
  	if (!(tp->t_state & ISOPEN))
  		return;
***************
*** 253,263
  
  	if (stat & CARR_OFF) {
  		if (tp->t_state & CARR_ON) {
! 			if (tp->t_chan)
! 				scontrol(tp->t_chan, M_SIG, SIGHUP);
! 			else
! 				signal(tp->t_pgrp, SIGHUP);
! 			flushtty(tp);
  		}
  		tp->t_state &= ~CARR_ON;
  		return;

--- 271,286 -----
  
  	if (stat & CARR_OFF) {
  		if (tp->t_state & CARR_ON) {
! 			if (!(tp->t_state & DKCALL)) {
! 				if (tp->t_chan)
! 					scontrol(tp->t_chan, M_SIG, SIGHUP);
! 				else
! 					signal(tp->t_pgrp, SIGHUP);
! 				flushtty(tp);
! 				tp->t_state &= ~CARR_ON;
! 				return;
! 			}
! 			tp->t_state &= ~CARR_ON;
  		}
  	}
  	else {
***************
*** 259,266
  				signal(tp->t_pgrp, SIGHUP);
  			flushtty(tp);
  		}
- 		tp->t_state &= ~CARR_ON;
- 		return;
  	}
  	if (!(tp->t_state & CARR_ON)) {
  		tp->t_state |= CARR_ON;

--- 282,287 -----
  			}
  			tp->t_state &= ~CARR_ON;
  		}
  	}
  	else {
  		if (!(tp->t_state & CARR_ON)) {
***************
*** 262,271
  		tp->t_state &= ~CARR_ON;
  		return;
  	}
! 	if (!(tp->t_state & CARR_ON)) {
! 		tp->t_state |= CARR_ON;
! 		wakeup(&tp->t_rawq);
! 		return;
  	}
  	if (stat & EXA) {
  		/*

--- 283,294 -----
  			tp->t_state &= ~CARR_ON;
  		}
  	}
! 	else {
! 		if (!(tp->t_state & CARR_ON)) {
! 			tp->t_state |= CARR_ON;
! 			wakeup(&tp->t_rawq);
! 			return;
! 		}
  	}
  	if (stat & EXA) {
  		/*
***************
*** 295,302
  	register struct ccb *ccb;
  	register c;
  
! 	tp = &vdu[dev];
! 	waddr = vduaddr[dev] + 1;
  	if (!(tp->t_state & ISOPEN))
  		return;
  trace(1<<16, "wint", waddr);

--- 318,325 -----
  	register struct ccb *ccb;
  	register c;
  
! 	tp = &vdu[minor(dev)];
! 	waddr = vduaddr[minor(dev)] + 1;
  	if (!(tp->t_state & ISOPEN))
  		return;
  trace(1<<16, "wint", waddr);


----------------
						J. Eric Roskos
						Perkin-Elmer Corp.
						Southern Development Center
						<jer@peora.UUCP>