Subject: Changes to com.c for bidirectional use
To: None <current-users@sun-lamp.cs.berkeley.edu>
From: Bakul Shah <bakul@netcom.com>
List: current-users
Date: 08/20/1994 23:28:40
Changes to com.c (from Aug 1) for bidirectional use are appended
at the end of this message. Here is a quick sketch of why, what
and how. One caveat: the code has been tested only on one
machine. Use at your own risk etc. If you find any bugs please
let me know.
--Bakul Shah
WHY:
The goal is to allow the use of a serial line for outgoing calls
(for cu, kermit etc.) as well as for incoming calls (i.e. for logging
in). In such a use typically the init blocks in open() of an incoming
device. When an incoming call raises the carrier, open() completes
and getty is execed. We would like to callout over the same line
if there is no incoming call. While our outgoing call is active,
we do not want getty to get activated and while getty/login/shell
are active we do not want an outgoing call to succeed. The
bidirectional use changes make this possible. The advantage of a
kernel based solution is that the window in which incoming calls
are not recognized is much smaller; in fact someone is *always*
`tending' the line. The disadvantage is that every serial driver
has to be changed to affect this functionality. This and some other
bits from many serial drivers are good candidates for factoring out.
WHAT:
Outgoing lines are named /dev/cua00, /dev/cua01... Incoming lines
are named /dev/tty00, /dev/tty01... If minor number of /dev/ttyXX
is M, minor number of the correspoding /dev/cuaXX is M+128.
First, note that the new behavior can be completely disregarded if
you don't plan to use /dev/cuaXX. In fact, *while a cuaXX line is
not in use*, behavior of the new driver is identical to that of the
old driver.
New behavior:
1. ttyXX and cuaXX are mutually exclusive devices.
2. cuaXX may also be opened while a ttyXX open() call is blocked,
waiting for a carrier detect.
3. When cuaXX is opened, the CLOCAL flag is set. This prevents a
carrier drop from sending the process a SIGUP signal.
4. While cuaXX is active, any ttyXX open() remains blocked.
5. ttyXX open() goes back to waiting on carrier detect once the last
cuaXX close is done.
6. The last close on either cuaXX or ttyXX drops DTR if the SOFTCARR
flag is not set.
7. The last close on cuaXX raises DTR after a second if there is a
blocked ttyXX open. Dropping DTR for a second allows a connected
modem to hangup and reset to defaults and then go back to answering
calls. The effect is as if the intervening use of cuaXX had
never happened.
8. Any number of concurrent open()s may succeed on either ttyXX (as
before) or cuaXX provided the TS_XCLUDE flag is not set and
subject to # 1 above.
HOW:
*** com.c-save Mon Aug 1 13:13:56 1994
--- com.c Sat Aug 20 16:52:52 1994
***************
*** 77,82 ****
--- 77,85 ----
#define COM_SW_CLOCAL 0x02
#define COM_SW_CRTSCTS 0x04
#define COM_SW_MDMBUF 0x08
+ #define COM_SW_OUTGOING 0x10
+ #define COM_SW_INCOMING 0x20
+ #define COM_SW_WAIT 0x40
u_char sc_msr, sc_mcr;
};
/* XXXX should be in com_softc, but not ready for that yet */
***************
*** 110,116 ****
extern int kgdb_debug_init;
#endif
! #define COMUNIT(x) (minor(x))
#define bis(c, b) do { const register u_short com_ad = (c); \
outb(com_ad, inb(com_ad) | (b)); } while(0)
--- 113,122 ----
extern int kgdb_debug_init;
#endif
! #define COMUNIT(x) (minor(x) & ~0x80)
! #define OUTGOING(x) ((x) & 0x80)
! #define IS_OUTGOING(sc) ((sc)->sc_swflags & COM_SW_OUTGOING)
! #define IS_INCOMING(sc) ((sc)->sc_swflags & COM_SW_INCOMING)
#define bis(c, b) do { const register u_short com_ad = (c); \
outb(com_ad, inb(com_ad) | (b)); } while(0)
***************
*** 320,339 ****
return EBUSY;
}
! /* wait for carrier if necessary */
! if ((flag & O_NONBLOCK) == 0)
! while ((tp->t_cflag & CLOCAL) == 0 &&
! (tp->t_state & TS_CARR_ON) == 0) {
! tp->t_state |= TS_WOPEN;
! error = ttysleep(tp, (caddr_t)&tp->t_rawq,
! TTIPRI | PCATCH, ttopen, 0);
! if (error) {
! /* XXX should turn off chip if we're the
! only waiter */
! splx(s);
! return error;
! }
}
splx(s);
return (*linesw[tp->t_line].l_open)(dev, tp);
--- 326,373 ----
return EBUSY;
}
! if (OUTGOING(dev)) {
! /* outgoing open may succeed only if incoming open has not */
! if (IS_INCOMING(sc)) {
! splx(s);
! return EBUSY;
! }
! tp->t_cflag |= CLOCAL;
! sc->sc_swflags |= COM_SW_OUTGOING;
!
! /* wakeup the blocked incoming open to go sleep elsewhere */
! if (sc->sc_swflags & COM_SW_WAIT && tp->t_state & TS_WOPEN)
! wakeup((caddr_t)&tp->t_rawq);
! } else {
! /* incoming open may succeed only if outgoing open has not */
! if (IS_OUTGOING(sc)) {
! splx(s);
! return EBUSY;
}
+
+ /* wait for carrier if necessary */
+ if ((flag & O_NONBLOCK) == 0)
+ while ((tp->t_cflag & CLOCAL) == 0 &&
+ (tp->t_state & TS_CARR_ON) == 0) {
+ tp->t_state |= TS_WOPEN;
+ sc->sc_swflags |= COM_SW_WAIT;
+ error = ttysleep(tp, (caddr_t)&tp->t_rawq,
+ TTIPRI | PCATCH, ttopen, 0);
+ if (error) {
+ /* XXX should turn off chip if we're the
+ only waiter */
+ splx(s);
+ return error;
+ }
+ /* wait elsewhere if outgoing open is active */
+ while (IS_OUTGOING(sc)) {
+ (void)ttysleep(tp,
+ (caddr_t)&sc->sc_swflags,
+ TTIPRI | PCATCH, ttopen, 0);
+ }
+ }
+ sc->sc_swflags |= COM_SW_INCOMING;
+ }
splx(s);
return (*linesw[tp->t_line].l_open)(dev, tp);
***************
*** 358,367 ****
{
bic(iobase + com_cfcr, CFCR_SBREAK);
outb(iobase + com_ier, 0);
if (tp->t_cflag & HUPCL &&
! (sc->sc_swflags & COM_SW_SOFTCAR) == 0)
/* XXX perhaps only clear DTR */
outb(iobase + com_mcr, 0);
}
ttyclose(tp);
#ifdef notyet /* XXXX */
--- 392,420 ----
{
bic(iobase + com_cfcr, CFCR_SBREAK);
outb(iobase + com_ier, 0);
+
if (tp->t_cflag & HUPCL &&
! (sc->sc_swflags & COM_SW_SOFTCAR) == 0)
/* XXX perhaps only clear DTR */
outb(iobase + com_mcr, 0);
+
+ if (OUTGOING(dev)) {
+ /* drop DTR regardless of other flags */
+ outb(iobase + com_mcr, sc->sc_mcr &~ MCR_DTR);
+ sc->sc_swflags &= ~COM_SW_OUTGOING;
+ if (sc->sc_swflags & COM_SW_WAIT) {
+ /* wait a while before allowing incoming open */
+ (void)tsleep((caddr_t)&sc->sc_swflags+1,
+ TTIPRI | PCATCH, "comclose", hz);
+ /* setup for the waiting incoming open...*/
+ tp->t_cflag &= ~(MDMBUF|CLOCAL);
+ tp->t_state &= ~TS_ISOPEN;
+ sc->sc_mcr = MCR_DTR | MCR_RTS;
+ outb(iobase + com_mcr, sc->sc_mcr);
+ return 0;
+ }
+ } else
+ sc->sc_swflags &= ~COM_SW_INCOMING;
}
ttyclose(tp);
#ifdef notyet /* XXXX */
------------------------------------------------------------------------------