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 + -
显示快捷键?