dan (08/19/82)
I have received requests for the code to implement the scheme of line turn around mentioned in a previous article under net.unix-wizards. One should refer to that article for the reasons for each piece of code. Since there are so many tty drivers around it seemed reasonable to present the code in a commentary fashion rather than any diff listing or our driver in its entirety. In the v7 tty.c, there is an XCLUDE bit; this code might be thought of as an extension of that facilty. *** In ttyopen(), A news process group is being started by a fork of init. These should block if the line is locked. One can, of course, sleep on some other address than tp->t_state. *** pp = u.u_procp; if (pp->p_pgrp == 0 && pp->p_pid != 1) { pp->p_pgrp = pp->p_pid; u.u_ttyp = tp; /* u.u_ttyd set up by openi in fio.c */ #ifdef EXCLUDE if (tp->t_state&EXCLUDE) { spl5(); while (tp->t_state&EXCLUDE) sleep(&tp->t_state, TTIPRI); spl0(); u.u_error = EBUSY; return; } #endif if (tp->t_pgrp) { signal(tp->t_pgrp, SIGHUP); flushtty(tp); } tp->t_pgrp = pp->p_pid; } *** Also in ttyopen(), If the inode of the device entry has its x-bit set, call ttlock() to lock the line. In our application, /dev/cul0 has its x-bit set and is a different inode than its /dev/tty?. *** #ifdef EXCLUDE if ((fp = getf(u.u_ar0[R0])) == NULL) return; if (fp->f_inode->i_mode&IEXEC) ttlock(tp); #endif tp->t_state =& ~WOPEN; tp->t_state =| ISOPEN; } ttyclose(tp) register struct tty *tp; { *** In ttyclose(), The basic issue is to see if we should really close the device. If the line is locked, then only the process group which set the lock is allowed to close it. The line is also unlocked if it is closed. A bit of a kluge is used here and in ttlock(). The process group t_pgrp is stored by ttlock() as the negative of pp->p_pgrp, if the process is not a fork of init. The point is to try to avoid getting SIGHUP upon reopening and carrier drops, while still knowing which p_pgrp set the lock. *** #ifdef EXCLUDE if ((tp->t_state&EXCLUDE) == 0 || abs(tp->t_pgrp) == u.u_procp->p_pgrp) { #endif if (tp->t_state&CARR_ON) wflushtty(tp); else flushtty(tp); #ifdef EXCLUDE } spl5(); if (tp->t_state&EXCLUDE) if (abs(tp->t_pgrp) == u.u_procp->p_pgrp) ttunlock(tp); else { spl0(); return(1); } #endif tp->t_state &= (CARR_ON|SSTART|EXCLUDE); #ifdef EXCLUDE spl0(); #endif return(0); } *** Then in ttysgtty()/ttioctl(), One needs a way of calling ttlock() and ttunlock() from user programs. *** #ifdef EXCLUDE case(TTLOCK): ttlock(tp); break; case(TTUNLOCK): ttunlock(tp); break; #endif default: u.u_error = EINVAL; } } *** Finally the short routines ttlock() and ttunlock() *** #ifdef EXCLUDE ttlock(tp) register struct tty *tp; { register struct proc *pp; pp = u.u_procp; spl5(); *** Slight subtlety, members of the locking p_pgrp should be able to do reopens. while ((tp->t_state&EXCLUDE) && abs(tp->t_pgrp) != pp->p_pgrp) sleep(&tp->t_state, TTIPRI); tp->t_state |= EXCLUDE; *** If not fork of init if (tp->t_pgrp != pp->p_pgrp) { if (tp->t_pgrp > 0) signal(tp->t_pgrp, SIGHUP); tp->t_pgrp = - pp->p_pgrp; } spl0(); } ttunlock(tp) register struct tty *tp; { tp->t_state &= ~EXCLUDE; wakeup(&tp->t_state); if (tp->t_pgrp < 0) tp->t_pgrp = 0; } #endif *** Have fun, Dan Ts'o ...decvax!cca!ima!n44a!dan