lpt.c

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

C
956
字号
/* * Copyright (c) 1990 William F. Jolitz, TeleMuse * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in the *    documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software *    must display the following acknowledgement: *	This software is a component of "386BSD" developed by *	William F. Jolitz, TeleMuse. * 4. Neither the name of the developer nor the name "386BSD" *    may be used to endorse or promote products derived from this software *    without specific prior written permission. * * THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ * AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS * SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT. * THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT * NOT MAKE USE OF THIS WORK. * * FOR USERS WHO WISH TO UNDERSTAND THE 386BSD SYSTEM DEVELOPED * BY WILLIAM F. JOLITZ, WE RECOMMEND THE USER STUDY WRITTEN * REFERENCES SUCH AS THE  "PORTING UNIX TO THE 386" SERIES * (BEGINNING JANUARY 1991 "DR. DOBBS JOURNAL", USA AND BEGINNING * JUNE 1991 "UNIX MAGAZIN", GERMANY) BY WILLIAM F. JOLITZ AND * LYNNE GREER JOLITZ, AS WELL AS OTHER BOOKS ON UNIX AND THE * ON-LINE 386BSD USER MANUAL BEFORE USE. A BOOK DISCUSSING THE INTERNALS * OF 386BSD ENTITLED "386BSD FROM THE INSIDE OUT" WILL BE AVAILABLE LATE 1992. * * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED.  IN NO EVENT SHALL THE DEVELOPER BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * *	from: unknown origin, 386BSD 0.1 *	From Id: lpt.c,v 1.55.2.1 1996/11/12 09:08:38 phk Exp *	From Id: nlpt.c,v 1.14 1999/02/08 13:55:43 des Exp *	$Id: lpt.c,v 1.2.2.1 1999/04/24 02:20:22 obrien Exp $ *//* * Device Driver for AT parallel printer port * Written by William Jolitz 12/18/90 *//* * Updated for ppbus by Nicolas Souchu * [Mon Jul 28 1997] */#ifdef KERNEL#include "opt_devfs.h"#include <sys/param.h>#include <sys/systm.h>#include <sys/conf.h>#include <sys/buf.h>#include <sys/kernel.h>#include <sys/uio.h>#include <sys/syslog.h>#ifdef DEVFS#include <sys/devfsext.h>#endif /*DEVFS*/#include <sys/malloc.h>#include <machine/clock.h>#include <machine/lpt.h>#endif /*KERNEL*/#include <dev/ppbus/ppbconf.h>#include <dev/ppbus/ppb_1284.h>#include <dev/ppbus/lpt.h>#include "opt_lpt.h"#ifndef LPT_DEBUG#define lprintf(args)#else#define lprintf(args)						\		do {						\			if (lptflag)				\				printf args;			\		} while (0)static int volatile lptflag = 1;#endif#define	LPINITRDY	4	/* wait up to 4 seconds for a ready */#define	LPTOUTINITIAL	10	/* initial timeout to wait for ready 1/10 s */#define	LPTOUTMAX	1	/* maximal timeout 1 s */#define	LPPRI		(PZERO+8)#define	BUFSIZE		1024#define	BUFSTATSIZE	32#define	LPTUNIT(s)	((s)&0x03)#define	LPTFLAGS(s)	((s)&0xfc)struct lpt_data {	unsigned short lpt_unit;	struct ppb_device lpt_dev;	short	sc_state;	/* default case: negative prime, negative ack, handshake strobe,	   prime once */	u_char	sc_control;	char	sc_flags;#define LP_POS_INIT	0x04	/* if we are a postive init signal */#define LP_POS_ACK	0x08	/* if we are a positive going ack */#define LP_NO_PRIME	0x10	/* don't prime the printer at all */#define LP_PRIMEOPEN	0x20	/* prime on every open */#define LP_AUTOLF	0x40	/* tell printer to do an automatic lf */#define LP_BYPASS	0x80	/* bypass  printer ready checks */	struct	buf *sc_inbuf;	struct	buf *sc_statbuf;	short	sc_xfercnt ;	char	sc_primed;	char	*sc_cp ;	u_short	sc_irq ;	/* IRQ status of port */#define LP_HAS_IRQ	0x01	/* we have an irq available */#define LP_USE_IRQ	0x02	/* we are using our irq */#define LP_ENABLE_IRQ	0x04	/* enable IRQ on open */#define LP_ENABLE_EXT	0x10	/* we shall use advanced mode when possible */	u_char	sc_backoff ;	/* time to call lptout() again */#ifdef DEVFS	void	*devfs_token;	void	*devfs_token_ctl;#endif};static int	nlpt = 0;#define MAXLPT	8			/* XXX not much better! */static struct lpt_data *lptdata[MAXLPT];#define LPT_NAME	"lpt"		/* our official name */static timeout_t lptout;static int	lpt_port_test(struct lpt_data *sc, u_char data, u_char mask);static int	lpt_detect(struct lpt_data *sc);/* * Make ourselves visible as a ppbus driver */static struct ppb_device	*lptprobe(struct ppb_data *ppb);static int			lptattach(struct ppb_device *dev);static void			lptintr(int unit);static void			lpt_drvinit(void *unused);static void			lpt_intr(int unit);	/* without spls */#ifdef KERNELstatic struct ppb_driver lptdriver = {    lptprobe, lptattach, LPT_NAME};DATA_SET(ppbdriver_set, lptdriver);#endif /* KERNEL *//* bits for state */#define	OPEN		(1<<0)	/* device is open */#define	ASLP		(1<<1)	/* awaiting draining of printer */#define	EERROR		(1<<2)	/* error was received from printer */#define	OBUSY		(1<<3)	/* printer is busy doing output */#define LPTOUT		(1<<4)	/* timeout while not selected */#define TOUT		(1<<5)	/* timeout while not selected */#define LPTINIT		(1<<6)	/* waiting to initialize for open */#define INTERRUPTED	(1<<7)	/* write call was interrupted */#define HAVEBUS		(1<<8)	/* the driver owns the bus *//* status masks to interrogate printer status */#define RDY_MASK	(LPS_SEL|LPS_OUT|LPS_NBSY|LPS_NERR)	/* ready ? */#define LP_READY	(LPS_SEL|LPS_NBSY|LPS_NERR)/* Printer Ready condition  - from lpa.c *//* Only used in polling code */#define	LPS_INVERT	(LPS_NBSY | LPS_NACK |           LPS_SEL | LPS_NERR)#define	LPS_MASK	(LPS_NBSY | LPS_NACK | LPS_OUT | LPS_SEL | LPS_NERR)#define	NOT_READY(lpt)	((ppb_rstr(&(lpt)->lpt_dev)^LPS_INVERT)&LPS_MASK)#define	MAX_SLEEP	(hz*5)	/* Timeout while waiting for device ready */#define	MAX_SPIN	20	/* Max delay for device ready in usecs */static	d_open_t	lptopen;static	d_close_t	lptclose;static	d_write_t	lptwrite;static	d_read_t	lptread;static	d_ioctl_t	lptioctl;#define CDEV_MAJOR 16static struct cdevsw lpt_cdevsw = 	{ lptopen,	lptclose,	lptread,	lptwrite,	/*16*/	  lptioctl,	nullstop,	nullreset,	nodevtotty,	/* lpt */	  seltrue,	nommap,		nostrat,	LPT_NAME,	NULL,	-1 };static intlpt_request_ppbus(struct lpt_data *sc, int how){	int error;	if (sc->sc_state & HAVEBUS)		return (0);	/* we have the bus only if the request succeded */	if ((error = ppb_request_bus(&sc->lpt_dev, how)) == 0)		sc->sc_state |= HAVEBUS;	return (error);}static intlpt_release_ppbus(struct lpt_data *sc){	ppb_release_bus(&sc->lpt_dev);	sc->sc_state &= ~HAVEBUS;	return (0);}/* * Internal routine to lptprobe to do port tests of one byte value */static intlpt_port_test(struct lpt_data *sc, u_char data, u_char mask){	int	temp, timeout;	data = data & mask;	ppb_wdtr(&sc->lpt_dev, data);	timeout = 10000;	do {		DELAY(10);		temp = ppb_rdtr(&sc->lpt_dev) & mask;	}	while (temp != data && --timeout);	lprintf(("out=%x\tin=%x\ttout=%d\n", data, temp, timeout));	return (temp == data);}/* * Probe simplified by replacing multiple loops with a hardcoded * test pattern - 1999/02/08 des@freebsd.org * * New lpt port probe Geoff Rehmet - Rhodes University - 14/2/94 * Based partially on Rod Grimes' printer probe * * Logic: *	1) If no port address was given, use the bios detected ports *	   and autodetect what ports the printers are on. *	2) Otherwise, probe the data port at the address given, *	   using the method in Rod Grimes' port probe. *	   (Much code ripped off directly from Rod's probe.) * * Comments from Rod's probe: * Logic: *	1) You should be able to write to and read back the same value *	   to the data port.  Do an alternating zeros, alternating ones, *	   walking zero, and walking one test to check for stuck bits. * *	2) You should be able to write to and read back the same value *	   to the control port lower 5 bits, the upper 3 bits are reserved *	   per the IBM PC technical reference manauls and different boards *	   do different things with them.  Do an alternating zeros, alternating *	   ones, walking zero, and walking one test to check for stuck bits. * *	   Some printers drag the strobe line down when the are powered off * 	   so this bit has been masked out of the control port test. * *	   XXX Some printers may not like a fast pulse on init or strobe, I *	   don't know at this point, if that becomes a problem these bits *	   should be turned off in the mask byte for the control port test. * *	   We are finally left with a mask of 0x14, due to some printers *	   being adamant about holding other bits high ........ * *	   Before probing the control port, we write a 0 to the data port - *	   If not, some printers chuck out garbage when the strobe line *	   gets toggled. * *	3) Set the data and control ports to a value of 0 * *	This probe routine has been tested on Epson Lx-800, HP LJ3P, *	Epson FX-1170 and C.Itoh 8510RM *	printers. *	Quick exit on fail added. */static intlpt_detect(struct lpt_data *sc){	static u_char	testbyte[18] = {		0x55,			/* alternating zeros */		0xaa,			/* alternating ones */		0xfe, 0xfd, 0xfb, 0xf7,		0xef, 0xdf, 0xbf, 0x7f,	/* walking zero */		0x01, 0x02, 0x04, 0x08,		0x10, 0x20, 0x40, 0x80	/* walking one */	};	int		i, error, status;	status = 1;				/* assume success */	if ((error = lpt_request_ppbus(sc, PPB_DONTWAIT))) {		printf(LPT_NAME ": cannot alloc ppbus (%d)!\n", error);		status = 0;		goto end_probe;	}	for (i = 0; i < 18 && status; i++)		if (!lpt_port_test(sc, testbyte[i], 0xff)) {			status = 0;			goto end_probe;		}end_probe:	/* write 0's to control and data ports */	ppb_wdtr(&sc->lpt_dev, 0);	ppb_wctr(&sc->lpt_dev, 0);	lpt_release_ppbus(sc);	return (status);}/* * lptprobe() */static struct ppb_device *lptprobe(struct ppb_data *ppb){	struct lpt_data *sc;	sc = (struct lpt_data *) malloc(sizeof(struct lpt_data),							M_TEMP, M_NOWAIT);	if (!sc) {		printf(LPT_NAME ": cannot malloc!\n");		return (0);	}	bzero(sc, sizeof(struct lpt_data));	lptdata[nlpt] = sc;	/*	 * lpt dependent initialisation.	 */	sc->lpt_unit = nlpt;	/*	 * ppbus dependent initialisation.	 */	sc->lpt_dev.id_unit = sc->lpt_unit;	sc->lpt_dev.name = lptdriver.name;	sc->lpt_dev.ppb = ppb;	sc->lpt_dev.intr = lptintr;	/*	 * Now, try to detect the printer.	 */	if (!lpt_detect(sc)) {		free(sc, M_TEMP);		return (0);	}	/* Ok, go to next device on next probe */	nlpt ++;	return (&sc->lpt_dev);}static intlptattach(struct ppb_device *dev){	struct lpt_data *sc = lptdata[dev->id_unit];	int error;	/*	 * Report ourselves	 */	printf(LPT_NAME "%d: <generic printer> on ppbus %d\n",	       dev->id_unit, dev->ppb->ppb_link->adapter_unit);	sc->sc_primed = 0;	/* not primed yet */	if ((error = lpt_request_ppbus(sc, PPB_DONTWAIT))) {		printf(LPT_NAME ": cannot alloc ppbus (%d)!\n", error);		return (0);	}	ppb_wctr(&sc->lpt_dev, LPC_NINIT);	/* check if we can use interrupt, should be done by ppc stuff */	lprintf(("oldirq %x\n", sc->sc_irq));	if (ppb_get_irq(&sc->lpt_dev)) {		sc->sc_irq = LP_HAS_IRQ | LP_USE_IRQ | LP_ENABLE_IRQ;		printf(LPT_NAME "%d: Interrupt-driven port\n", dev->id_unit);	} else {		sc->sc_irq = 0;		lprintf((LPT_NAME "%d: Polled port\n", dev->id_unit));	}	lprintf(("irq %x\n", sc->sc_irq));	lpt_release_ppbus(sc);#ifdef DEVFS	sc->devfs_token = devfs_add_devswf(&lpt_cdevsw,		dev->id_unit, DV_CHR,		UID_ROOT, GID_WHEEL, 0600, LPT_NAME "%d", dev->id_unit);	sc->devfs_token_ctl = devfs_add_devswf(&lpt_cdevsw,		dev->id_unit | LP_BYPASS, DV_CHR,		UID_ROOT, GID_WHEEL, 0600, LPT_NAME "%d.ctl", dev->id_unit);#endif	return (1);}static voidlptout(void *arg){	struct lpt_data *sc = arg;	int pl;	lprintf(("T %x ", ppb_rstr(&sc->lpt_dev)));	if (sc->sc_state & OPEN) {		sc->sc_backoff++;		if (sc->sc_backoff > hz/LPTOUTMAX)			sc->sc_backoff = sc->sc_backoff > hz/LPTOUTMAX;		timeout(lptout, (caddr_t)sc, sc->sc_backoff);	} else		sc->sc_state &= ~TOUT;	if (sc->sc_state & EERROR)		sc->sc_state &= ~EERROR;	/*	 * Avoid possible hangs do to missed interrupts	 */	if (sc->sc_xfercnt) {		pl = spltty();		lpt_intr(sc->lpt_unit);		splx(pl);	} else {		sc->sc_state &= ~OBUSY;		wakeup((caddr_t)sc);	}}/* * 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. */static	intlptopen(dev_t dev, int flags, int fmt, struct proc *p){	struct lpt_data *sc;	int s;	int trys, err;	u_int unit = LPTUNIT(minor(dev));

⌨️ 快捷键说明

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