4.4BSD/usr/src/sys/vax/datakit/dk.c

Compare this file to the similar file:
Show the results in this format:

/*
 * Datakit driver
 *	SCCSID[] = "@(#)dk.c	2.1 DKHOST 84/07/03"
 */

#include "datakit.h"
#if NDATAKIT>0

#include "../include/pte.h"
#include "sys/param.h"
#include "sys/signal.h"
#include "sys/errno.h"
#include "sys/conf.h"
#include "sys/user.h"
#include "sys/ioctl.h"
#include "sys/tty.h"
#include "sys/vnode.h"
#include "sys/file.h"
#include "sys/systm.h"
#include "sys/proc.h"
#include "sys/mbuf.h"
#include "sys/buf.h"
#include "sys/uio.h"
#include "sys/kernel.h"
#include "sys/dkit.h"
#include "sys/dkcmc.h"
#include "sys/dk.h"
#include "sys/dkdev.h"
#include "sys/syslog.h"

extern int dkdebug ;

#define DKBUFUSE	5	/* max buffers /channel */


int	dk_nchan	= NDATAKIT;
struct	dkdev		dkdev[NDATAKIT];
struct	dksetupreq	*dkreq[NDATAKIT];




#ifdef	MONITOR
int dummy ;
int *DKP2 = &dummy ;
#define	M_ON(a)		*DKP2 |= (a)
#define	M_OFF(a)	*DKP2 &= ~(a)

#define	Mread	0400
#define Mrslp	01000
#define	Mrcpy	02000
#define	Mwrite	04000
#define	Mwcpy	010000

#else
#define	M_ON(a)
#define	M_OFF(a)
#endif



/*ARGSUSED*/
dkioctl(dev, cmd, data, flag)
register caddr_t data;
{
	register struct	dkdev	*tp;
	register chanstat ;
	int	chan, sp_chan;
	int	s, error = 0;
	register short	*pp ;
	struct dkdev *tsp;
	extern dkidone() ;
	struct diocdial dialreq;
	extern int commchan;

	chan = dev = minor(dev);
	tp = &dkdev[chan];
	pp = (short *) data;
	switch(cmd) {
	case DIOCEXCL:
		tp->d_state |= DKXCLUDE ;
		break ;
	case DIOCNXCL:
		tp->d_state &= ~DKXCLUDE ;
		break ;
	case DIOCSETK:
		dkdebug = pp[0] ;
		break;
	case DIOCQQABO:
		pp[0] = tp->d_rresid;
		pp[1] = tp->d_rdone;
		pp[2] = tp->d_rctl;
		break ;
	case DIOCRMODE:
		if (pp[0] & DKR_TIME)
			tp->d_rmode = (DKR_TIME | DKR_BLOCK);
		else tp->d_rmode = pp[0] ;
		break ;
	case DIOCXCTL:
		tp->d_xctl = pp[0] ;
		break ;
	case DIOCFLUSH:
		dk_cmd(chan, DKC_XINIT|DKC_FLUSH);
		break;
	case KIOCINIT:
		dk_cmd(chan, DKC_XINIT);
		break;
	case DIOCXWIN:
		return dk_winsize(chan, (struct diocxwin *)data);
	case DIOCRESET:
		if (chan != 1 && chan != pp[0]) return EACCES;
		if (pp[0] > 1 && pp[0] < commchan) return EINVAL;
		if (pp[0] < 0 || pp[0] >= dk_nchan) return EINVAL;
		if (pp[0] == 0) return -dk_close(0);
		else dk_reset(pp[0]);
		break;
	case DIOCCTYPE:
		if (tp->d_ctype == NULL) {
			struct mbuf *m;

			MGET(m, M_WAIT, DKMT_CTYPE);
			if (m == NULL)
				return ENOBUFS;
			tp->d_ctype = mtod(m, struct diocctype *);
		}
		return bcopy(data, (caddr_t) tp->d_ctype, sizeof (struct diocctype));
	case DIOCINFO:
		((struct diocinfo *)data)->dioc_nchan = dk_nchan;
		((struct diocinfo *)data)->dioc_channum = chan;
		((struct diocinfo *)data)->dioc_commchan = commchan;
		break;
	case DIOCSTAT:
		if (*((int *)data) < 0 || *((int *)data) >= dk_nchan)
			return EINVAL;
		*((int *)data) = dk_status(*((int *)data));
		break;
	case FIONBIO:
		if (*(int *)data)
			tp->dc_state |= DK_NDELAY;
		else
			tp->dc_state &= ~DK_NDELAY;
		break;
	case FIOASYNC:
		if (*(int *)data)
			tp->dc_state |= DK_ASYNC;
		else
			tp->dc_state &= ~DK_ASYNC;
		break;
	case TIOCGPGRP:
		*(int *)data = tp->d_pgrp;
		break;
	case TIOCSPGRP:
		tp->d_pgrp = *(int *)data;
		break;

	/* splice chan to file descriptor */
	case DKIOCSPL:
		error = copyin(*(caddr_t *)data, (caddr_t) tp->d_param,
		    3*sizeof (short));
		if (error) return error;
		if ((error = dkgetdev(tp->d_param[0], &sp_chan)) <= 0)
			return error ;
		if (sp_chan == chan)
			return EINVAL ;
		tsp = &dkdev[sp_chan] ;
		tp->dc_state |= DKSETUP ;
		tsp->dc_state |= DKSETUP ;
		if (dk_splice(chan, sp_chan, dkidone, (caddr_t) tp,
			(caddr_t) tsp)) {
			tp->dc_state &= ~DKSETUP ;
			tsp->dc_state &= ~DKSETUP ;
			return EIO ;
		}
		s = spl5() ;
		error = 0;
		while (error == 0 && tp->dc_state & DKSETUP)
			error = tsleep((caddr_t)tp, TTOPRI, ttopen, 0);
		while (error == 0 && tsp->dc_state & DKSETUP)
			error = tsleep((caddr_t)tsp, TTOPRI, ttopen, 0);
		splx(s) ;
		if (error)
			return (error);
		if ((dk_status(chan) & DK_RESET) || (dk_status(sp_chan) & DK_RESET))
			return EIO ;
		if (tp->d_error || tsp->d_error) 
			return EIO ;
		error = copyout((caddr_t) tp->d_param, *(caddr_t *)data,
		    3*sizeof (short));
		if (error) return error;
		break ;

	case DIOCSWAIT:
		error = dksplwait(chan);
		break ;

	default:
		if ((cmd & DKIOCMASK) != DKIOCVAL) {
			return ENOTTY ;
		}
		if (cmd == DKIODIAL) {
			error = copyin(*(caddr_t *)data, (caddr_t) &dialreq,
			    sizeof (struct diocdial));
			if (error) return error;
			if (error = dkiodial(chan, dialreq.dialstring))
				return error;
			tp->dc_state |= DKSETUP ;
			chanstat = dk_setup(minor(dev), (int) DKIOCREQ, 0,
			0, 0, (int) u.u_uid, dkidone, (caddr_t)tp) ;
		}
		else {
			error = copyin(*(caddr_t *)data, (caddr_t) tp->d_param,
			    3*sizeof (short));
			if (error) return error;
			tp->dc_state |= DKSETUP ;
			chanstat = dk_setup(minor(dev), cmd, 0, 0, 0,
				(int) u.u_uid, dkidone, (caddr_t)tp) ;
		}
		if (chanstat) {
			tp->dc_state &= ~DKSETUP ;
			return (chanstat < 0 ? ECONNREFUSED : chanstat);
		}
		s = spl5() ;
		error = 0;
		while (error == 0 && tp->dc_state & DKSETUP)
			error = tsleep((caddr_t)(tp), TTOPRI, ttyout, 0) ;
		splx(s) ;
		if (error)
			return error;
		error = copyout((caddr_t) tp->d_param, *(caddr_t *)data,
		    3*sizeof (short));
		if (error) return error;
		if (dk_status(minor(dev)) & DK_RESET)
			return ENETRESET ;
		if (tp->d_error)
			return EIO ;
		break ;
	}
	return error;
}

#define DS_SIZE 64
static
dkiodial(chan, user_ds)
register char *user_ds;
{
	register caddr_t ds;
	register n;
	register struct mbuf *mb;
	int u_count;

	mb = m_get(M_WAIT, DKMT_DATA);
	if (mb == NULL) return ENOBUFS;
	ds = mtod(mb, caddr_t);
	for (u_count = 0; u_count < MLEN - 6; u_count++) {
		*ds = *user_ds;
		if (*ds == '\n' || *ds == '\0') break;
		ds++;
		user_ds++;
	}
	*ds = '\n';
	u_count++;

	/* add uid in char decimal */

	ds++;
	u_count++;
	for (n = u.u_uid; n /= 10; ds++) u_count++;
	for (n = u.u_uid;; ds--) {
		*ds = n % 10 + '0';
		if ((n /= 10) == 0) break;
	}

	mb->m_len = u_count;
	if (dk_xmit(chan, mb, 1, 0, (int (*)()) 0, (caddr_t) 0) == 0) {
		return(EIO);
	}
	else return(0);
}
/*
 * End action for ioctl completion
 */
/*ARGSUSED*/
dkidone(tp, chan, err, p0, p1, p2)
register struct dkdev *tp ;
short chan, p0, p1, p2 ;
{
	tp->d_error = err ;
	tp->d_param[0] = p0 ;
	tp->d_param[1] = p1 ;
	tp->d_param[2] = p2 ;
	tp->dc_state &= ~DKSETUP ;
	wakeup((caddr_t)tp) ;
}




/*ARGSUSED*/
dkopen(dev, flag)
{
	USES_VOP_LOCK;
	USES_VOP_UNLOCK;
	register struct	dkdev	*tp;
	register chan;
	register struct nameidata *ndp = &u.u_nd;	/* XXX */
	struct proc *p = u.u_procp;			/* XXX */
	struct vnode *vp;
	struct	file *fp;
	int	 m, error;

#ifdef lint
	(void) dk_xint(0, 0);
#endif
	dev = minor(dev);
	if (dev == 1) {
		return 0;	/* Maintenance channel */
	}

	chan = dev;
	if (chan >= dk_nchan) {
/* debug */	log(LOG_ERR, "dkopen bad: chan>=NDKCHANS : %d\n",chan);
		return ENXIO;
	}

	tp = &dkdev[chan];
	if ((tp->d_state & DKOPEN) == 0)
		tp->dc_state = 0 ;
	if (tp->d_state&DKXCLUDE && u.u_procp->p_ruid!=0) {
		return EBUSY;
	}

	if ((m = dk_open(chan, (int (*)()) NULL)) < 0)
		return -m;


	/*
	 * Channel 0 is reserved for maintenance.
	 * An open on channel 0 is interpreted as a request
	 * for an unused channel.
	 */
	if (chan==0) {
		char dname[30];

		chan = m ;
		tp = &dkdev[chan] ;
		tp->dc_state = 0 ;
		/*
		 * throw away vnode for dk0. (/dev/dk/dial)
		 * Build standard name of new one, and ask namei for it.
		 */
		fp = u.u_ofile[-1 - p->p_dupfd];

		dksnamer(dname, chan);
		/* log(LOG_ERR, "dname=%s chan=%d\n", dname, chan); */
		ndp->ni_nameiop = FOLLOW | LOOKUP | LOCKLEAF;
		ndp->ni_segflg = UIO_SYSSPACE;
		ndp->ni_dirp = dname;
		if (error = namei(ndp)) {
			(void) dk_close(chan) ;
			return (error);
		}

		/* Give back old one */
		vp = (struct vnode *) fp->f_data;
		VOP_LOCK(vp);
		vput(vp);

		vp = ndp->ni_vp;
		fp->f_data = (caddr_t) vp;
		VOP_UNLOCK(vp);
	}
	if ((tp->d_state & DKOPEN) == 0) {
		tp->d_state |= DKOPEN ;
		tp->dc_state = 0;
		tp->d_rmode = 0 ;
		tp->d_xctl = 0 ;
		tp->d_pgrp = 0;
	}
	tp->d_prot |= DpURP;
	return 0;
}

/* Policy decision here -- standard name of dk file known to this routine */
dksnamer(s, n) register char *s;
{
	register char *p = "/dev/dk/dk";

	while (*s++ = *p++)
		;
	s--;
	*s++ = '0' + (n/100); n %= 100;
	*s++ = '0' + (n/10); n %= 10;
	*s++ = '0' + n;
	*s = '\0';
}

/*
 * Close a channel:
 */

/*ARGSUSED*/
dkclose(dev, flag)
dev_t dev;
int flag;
{
	register struct	dkdev	*tp;
	extern wakeup() ;
	extern brelse() ;
	short	s, chan ;
	int i, cl = 0;

	chan = minor(dev);
	tp = &dkdev[chan];
	if (chan == 1) {
		return 0;	/* Maintenance channel */
	}
	s = spl5() ;
	if (u.u_signal[SIGKILL] != SIG_IGN) {	  /* detect close from exit() */
		while (tp->d_bufct) {
			tp->d_state |= DKWAIT ;
			if (tsleep((caddr_t)(&tp->d_state), TTOPRI, ttyout, 0))
				break;
		}
	}
	else if (tp->d_bufct)
		/* Hmm -- buffers queued.  Let's wait 15 seconds max */
		for (i = 0; tp->d_bufct && i < 15; i++) {
			tp->d_state |= DKWAIT ;
			if (tsleep((caddr_t)(&tp->d_state), TTOPRI, ttyout, hz))
				break;
		}
	splx(s) ;
	tp->dc_state = 0;
	tp->d_rmode = 0;
	tp->d_prot &= ~DpURP;
	if(!tp->d_prot){
		cl = dk_close(chan);
		(void) dk_takedown(chan);
		tp->d_state = 0;
	}
	return -cl;
}

dkread(dev, uio)
dev_t dev ;
struct uio *uio;
{
register struct dkdev *tp ;
int err;

	M_ON(Mread) ;
	tp = &dkdev[minor(dev)] ;
	err = dkuread(minor(dev), uio) ;
	tp->d_rresid = uio->uio_resid ;
	M_OFF(Mread) ;
	return err;
}


dkwrite(dev, uio)
struct uio *uio;
dev_t dev ;
{
	int err;

	M_ON(Mwrite) ;
	err = dkuwrite(minor(dev), uio) ;
	M_OFF(Mwrite) ;
	return err;
}

#endif