lpt.c

来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 1,398 行 · 第 1/3 页

C
1,398
字号
	/* check if we can use interrupt */	lprintf("oldirq %x\n", sc->sc_irq);	if (isdp->id_irq) {		sc->sc_irq = LP_HAS_IRQ | LP_USE_IRQ | LP_ENABLE_IRQ;		printf("lpt%d: Interrupt-driven port\n", unit);#ifdef INET		lpattach(sc, unit);#endif	} else {		sc->sc_irq = 0;		lprintf("lpt%d: Polled port\n", unit);	}	lprintf("irq %x\n", sc->sc_irq);	kdc_lpt[unit].kdc_state = DC_IDLE;#ifdef DEVFS/* XXX */ /* what to do about the flags in the minor number? */	sprintf(name,"lpt%d",unit);			/*	path name devsw minor type  uid gid perm*/	sc->devfs_token = devfs_add_devsw( "/", name, &lpt_cdevsw, unit,					DV_CHR, 0, 0, 0600);#endif	return (1);}/* * lptopen -- reset the printer, then wait until it's selected and not busy. *	If LP_BYPASS flag is selected, then we do not try to select the *	printer -- this is just used for passing ioctls. */intlptopen (dev_t dev, int flags, int fmt, struct proc *p){	struct lpt_softc *sc;	int s;	int trys, port;	u_int unit = LPTUNIT(minor(dev));	sc = lpt_sc + unit;	if ((unit >= NLPT) || (sc->sc_port == 0))		return (ENXIO);#ifdef INET	if (sc->sc_if.if_flags & IFF_UP)		return(EBUSY);#endif	if (sc->sc_state) {		lprintf("lp: still open %x\n", sc->sc_state);		return(EBUSY);	} else		sc->sc_state |= INIT;	sc->sc_flags = LPTFLAGS(minor(dev));	/* Check for open with BYPASS flag set. */	if (sc->sc_flags & LP_BYPASS) {		sc->sc_state = OPEN;		return(0);	}	s = spltty();	lprintf("lp flags 0x%x\n", sc->sc_flags);	port = sc->sc_port;	/* set IRQ status according to ENABLE_IRQ flag */	if (sc->sc_irq & LP_ENABLE_IRQ)		sc->sc_irq |= LP_USE_IRQ;	else		sc->sc_irq &= ~LP_USE_IRQ;	/* init printer */	if ((sc->sc_flags & LP_NO_PRIME) == 0) {		if((sc->sc_flags & LP_PRIMEOPEN) || sc->sc_primed == 0) {			outb(port+lpt_control, 0);			sc->sc_primed++;			DELAY(500);		}	}	outb (port+lpt_control, LPC_SEL|LPC_NINIT);	/* wait till ready (printer running diagnostics) */	trys = 0;	do {		/* ran out of waiting for the printer */		if (trys++ >= LPINITRDY*4) {			splx(s);			sc->sc_state = 0;			lprintf ("status %x\n", inb(port+lpt_status) );			return (EBUSY);		}		/* wait 1/4 second, give up if we get a signal */		if (tsleep ((caddr_t)sc, LPPRI|PCATCH, "lptinit", hz/4) !=		    EWOULDBLOCK) {			sc->sc_state = 0;			splx(s);			return (EBUSY);		}		/* is printer online and ready for output */	} while ((inb(port+lpt_status) & (LPS_SEL|LPS_OUT|LPS_NBSY|LPS_NERR)) !=		 (LPS_SEL|LPS_NBSY|LPS_NERR));	sc->sc_control = LPC_SEL|LPC_NINIT;	if (sc->sc_flags & LP_AUTOLF)		sc->sc_control |= LPC_AUTOL;	/* enable interrupt if interrupt-driven */	if (sc->sc_irq & LP_USE_IRQ)		sc->sc_control |= LPC_ENA;	outb(port+lpt_control, sc->sc_control);	sc->sc_state = OPEN;	kdc_lpt[unit].kdc_state = DC_BUSY;	sc->sc_inbuf = geteblk(BUFSIZE);	sc->sc_xfercnt = 0;	splx(s);	/* only use timeout if using interrupt */	lprintf("irq %x\n", sc->sc_irq);	if (sc->sc_irq & LP_USE_IRQ) {		sc->sc_state |= TOUT;		timeout ((timeout_func_t)lptout, (caddr_t)sc,			 (sc->sc_backoff = hz/LPTOUTINITIAL));	}	lprintf("opened.\n");	return(0);}static voidlptout (struct lpt_softc * sc){	int pl;	lprintf ("T %x ", inb(sc->sc_port+lpt_status));	if (sc->sc_state & OPEN) {		sc->sc_backoff++;		if (sc->sc_backoff > hz/LPTOUTMAX)			sc->sc_backoff = sc->sc_backoff > hz/LPTOUTMAX;		timeout ((timeout_func_t)lptout, (caddr_t)sc, sc->sc_backoff);	} else		sc->sc_state &= ~TOUT;	if (sc->sc_state & ERROR)		sc->sc_state &= ~ERROR;	/*	 * Avoid possible hangs do to missed interrupts	 */	if (sc->sc_xfercnt) {		pl = spltty();		lptintr(sc - lpt_sc);		splx(pl);	} else {		sc->sc_state &= ~OBUSY;		wakeup((caddr_t)sc);	}}/* * lptclose -- close the device, free the local line buffer. * * Check for interrupted write call added. */intlptclose(dev_t dev, int flags, int fmt, struct proc *p){	struct lpt_softc *sc = lpt_sc + LPTUNIT(minor(dev));	int port = sc->sc_port;	if(sc->sc_flags & LP_BYPASS)		goto end_close;	sc->sc_state &= ~OPEN;	kdc_lpt[minor(dev)].kdc_state = DC_IDLE;	/* if the last write was interrupted, don't complete it */	if((!(sc->sc_state  & INTERRUPTED)) && (sc->sc_irq & LP_USE_IRQ))		while ((inb(port+lpt_status) & (LPS_SEL|LPS_OUT|LPS_NBSY|LPS_NERR)) !=			(LPS_SEL|LPS_NBSY|LPS_NERR) || sc->sc_xfercnt)			/* wait 1/4 second, give up if we get a signal */			if (tsleep ((caddr_t)sc, LPPRI|PCATCH,				"lpclose", hz) != EWOULDBLOCK)				break;	outb(sc->sc_port+lpt_control, LPC_NINIT);	brelse(sc->sc_inbuf);end_close:	sc->sc_state = 0;	sc->sc_xfercnt = 0;	lprintf("closed.\n");	return(0);}/* * pushbytes() *	Workhorse for actually spinning and writing bytes to printer *	Derived from lpa.c *	Originally by ? * *	This code is only used when we are polling the port */static intpushbytes(struct lpt_softc * sc){	int spin, err, tic;	char ch;	int port = sc->sc_port;	lprintf("p");	/* loop for every character .. */	while (sc->sc_xfercnt > 0) {		/* printer data */		ch = *(sc->sc_cp);		sc->sc_cp++;		sc->sc_xfercnt--;		/*		 * Wait for printer ready.		 * Loop 20 usecs testing BUSY bit, then sleep		 * for exponentially increasing timeout. (vak)		 */		for (spin=0; NOT_READY(port+lpt_status) && spin<MAX_SPIN; ++spin)			DELAY(1);	/* XXX delay is NOT this accurate! */		if (spin >= MAX_SPIN) {			tic = 0;			while (NOT_READY(port+lpt_status)) {				/*				 * Now sleep, every cycle a				 * little longer ..				 */				tic = tic + tic + 1;				/*				 * But no more than 10 seconds. (vak)				 */				if (tic > MAX_SLEEP)					tic = MAX_SLEEP;				err = tsleep((caddr_t)sc, LPPRI,					"lptpoll", tic);				if (err != EWOULDBLOCK) {					return (err);				}			}		}		/* output data */		outb(port+lpt_data, ch);		/* strobe */		outb(port+lpt_control, sc->sc_control|LPC_STB);		outb(port+lpt_control, sc->sc_control);	}	return(0);}/* * lptwrite --copy a line from user space to a local buffer, then call * putc to get the chars moved to the output queue. * * Flagging of interrupted write added. */intlptwrite(dev_t dev, struct uio * uio, int ioflag){	register unsigned n;	int pl, err;	struct lpt_softc *sc = lpt_sc + LPTUNIT(minor(dev));	if(sc->sc_flags & LP_BYPASS) {		/* we can't do writes in bypass mode */		return(EPERM);	}	sc->sc_state &= ~INTERRUPTED;	while ((n = min(BUFSIZE, uio->uio_resid)) != 0) {		sc->sc_cp = sc->sc_inbuf->b_un.b_addr ;		uiomove(sc->sc_cp, n, uio);		sc->sc_xfercnt = n ;		while ((sc->sc_xfercnt > 0)&&(sc->sc_irq & LP_USE_IRQ)) {			lprintf("i");			/* if the printer is ready for a char, */			/* give it one */			if ((sc->sc_state & OBUSY) == 0){				lprintf("\nC %d. ", sc->sc_xfercnt);				pl = spltty();				lptintr(sc - lpt_sc);				(void) splx(pl);			}			lprintf("W ");			if (sc->sc_state & OBUSY)				if ((err = tsleep ((caddr_t)sc,					 LPPRI|PCATCH, "lpwrite", 0))) {					sc->sc_state |= INTERRUPTED;					return(err);				}		}		/* check to see if we must do a polled write */		if(!(sc->sc_irq & LP_USE_IRQ) && (sc->sc_xfercnt)) {			lprintf("p");			if((err = pushbytes(sc)))				return(err);		}	}	return(0);}/* * lptintr -- handle printer interrupts which occur when the printer is * ready to accept another char. * * do checking for interrupted write call. */voidlptintr(int unit){	struct lpt_softc *sc = lpt_sc + unit;	int port = sc->sc_port, sts;	int i;#ifdef INET	if(sc->sc_if.if_flags & IFF_UP) {	    lpintr(unit);	    return;	}#endif /* INET */	/*	 * Is printer online and ready for output?	 *	 * Avoid falling back to lptout() too quickly.  First spin-loop	 * to see if the printer will become ready ``really soon now''.	 */	for (i = 0;	     i < 100 &&	     ((sts=inb(port+lpt_status)) & RDY_MASK) != LP_READY;	     i++) ;	if ((sts & RDY_MASK) == LP_READY) {		sc->sc_state = (sc->sc_state | OBUSY) & ~ERROR;		sc->sc_backoff = hz/LPTOUTINITIAL;		if (sc->sc_xfercnt) {			/* send char */			/*lprintf("%x ", *sc->sc_cp); */			outb(port+lpt_data, *sc->sc_cp++) ;			outb(port+lpt_control, sc->sc_control|LPC_STB);			/* DELAY(X) */			outb(port+lpt_control, sc->sc_control);			/* any more data for printer */			if(--(sc->sc_xfercnt) > 0) return;		}		/*		 * No more data waiting for printer.		 * Wakeup is not done if write call was interrupted.		 */		sc->sc_state &= ~OBUSY;		if(!(sc->sc_state & INTERRUPTED))			wakeup((caddr_t)sc);		lprintf("w ");		return;	} else	{	/* check for error */		if(((sts & (LPS_NERR | LPS_OUT) ) != LPS_NERR) &&				(sc->sc_state & OPEN))			sc->sc_state |= ERROR;		/* lptout() will jump in and try to restart. */	}	lprintf("sts %x ", sts);}intlptioctl(dev_t dev, int cmd, caddr_t data, int flags, struct proc *p){	int	error = 0;        struct	lpt_softc *sc;        u_int	unit = LPTUNIT(minor(dev));	u_char	old_sc_irq;	/* old printer IRQ status */        sc = lpt_sc + unit;	switch (cmd) {	case LPT_IRQ :		if(sc->sc_irq & LP_HAS_IRQ) {			/*			 * NOTE:			 * If the IRQ status is changed,			 * this will only be visible on the			 * next open.			 *			 * If interrupt status changes,			 * this gets syslog'd.			 */			old_sc_irq = sc->sc_irq;			if(*(int*)data == 0)				sc->sc_irq &= (~LP_ENABLE_IRQ);			else				sc->sc_irq |= LP_ENABLE_IRQ;			if (old_sc_irq != sc->sc_irq )				log(LOG_NOTICE, "lpt%c switched to %s mode\n",					(char)unit+'0',					(sc->sc_irq & LP_ENABLE_IRQ)?					"interrupt-driven":"polled");		} else /* polled port */			error = EOPNOTSUPP;		break;	default:		error = ENODEV;	}	return(error);}#ifdef INETstatic voidlpattach (struct lpt_softc *sc, int unit){	struct ifnet *ifp = &sc->sc_if;	ifp->if_name = "lp";	ifp->if_unit = unit;	ifp->if_mtu = LPMTU;	ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST;	ifp->if_ioctl = lpioctl;	ifp->if_output = lpoutput;	ifp->if_type = IFT_PARA;	ifp->if_hdrlen = 0;	ifp->if_addrlen = 0;	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;	if_attach(ifp);	printf("lp%d: TCP/IP capable interface\n", unit);#if NBPFILTER > 0	bpfattach(&ifp->if_bpf, ifp, DLT_NULL, LPIPHDRLEN);#endif}/* * Build the translation tables for the LPIP (BSD unix) protocol. * We don't want to calculate these nasties in our tight loop, so we * precalculate them when we initialize. */static intlpinittables (void){    int i;    if (!txmith)	txmith = malloc(4*LPIPTBLSIZE, M_DEVBUF, M_NOWAIT);    if (!txmith)	return 1;    if (!ctxmith)	ctxmith = malloc(4*LPIPTBLSIZE, M_DEVBUF, M_NOWAIT);    if (!ctxmith)

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?