lpt.c revision 1.7
1/*	$NetBSD: lpt.c,v 1.7 1996/11/17 13:47:21 leo Exp $ */
2
3/*
4 * Copyright (c) 1996 Leo Weppelman
5 * Copyright (c) 1993, 1994 Charles Hannum.
6 * Copyright (c) 1990 William F. Jolitz, TeleMuse
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 *    must display the following acknowledgement:
19 *	This software is a component of "386BSD" developed by
20 *	William F. Jolitz, TeleMuse.
21 * 4. Neither the name of the developer nor the name "386BSD"
22 *    may be used to endorse or promote products derived from this software
23 *    without specific prior written permission.
24 *
25 * THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ
26 * AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS
27 * SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT.
28 * THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT
29 * NOT MAKE USE OF THIS WORK.
30 *
31 * FOR USERS WHO WISH TO UNDERSTAND THE 386BSD SYSTEM DEVELOPED
32 * BY WILLIAM F. JOLITZ, WE RECOMMEND THE USER STUDY WRITTEN
33 * REFERENCES SUCH AS THE  "PORTING UNIX TO THE 386" SERIES
34 * (BEGINNING JANUARY 1991 "DR. DOBBS JOURNAL", USA AND BEGINNING
35 * JUNE 1991 "UNIX MAGAZIN", GERMANY) BY WILLIAM F. JOLITZ AND
36 * LYNNE GREER JOLITZ, AS WELL AS OTHER BOOKS ON UNIX AND THE
37 * ON-LINE 386BSD USER MANUAL BEFORE USE. A BOOK DISCUSSING THE INTERNALS
38 * OF 386BSD ENTITLED "386BSD FROM THE INSIDE OUT" WILL BE AVAILABLE LATE 1992.
39 *
40 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND
41 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
43 * ARE DISCLAIMED.  IN NO EVENT SHALL THE DEVELOPER BE LIABLE
44 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
45 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
46 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
48 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
49 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50 * SUCH DAMAGE.
51 */
52
53/*
54 * Device Driver originally written for AT parallel printer port. Now
55 * drives the printer port on the YM2149.
56 */
57
58#include <sys/param.h>
59#include <sys/systm.h>
60#include <sys/proc.h>
61#include <sys/user.h>
62#include <sys/buf.h>
63#include <sys/kernel.h>
64#include <sys/ioctl.h>
65#include <sys/uio.h>
66#include <sys/device.h>
67#include <sys/conf.h>
68#include <sys/syslog.h>
69
70#include <machine/cpu.h>
71#include <machine/iomap.h>
72#include <machine/mfp.h>
73
74#include <atari/dev/ym2149reg.h>
75
76#define	TIMEOUT		hz*16	/* wait up to 16 seconds for a ready */
77#define	STEP		hz/4
78
79#define	LPTPRI		(PZERO+8)
80#define	LPT_BSIZE	1024
81
82#if !defined(DEBUG) || !defined(notdef)
83#define lprintf		if (0) printf
84#else
85#define lprintf		if (lptdebug) printf
86int lptdebug = 1;
87#endif
88
89struct lpt_softc {
90	struct device	sc_dev;
91	size_t		sc_count;
92	struct buf	*sc_inbuf;
93	u_char		*sc_cp;
94	int		sc_spinmax;
95	u_char		sc_state;
96#define	LPT_OPEN	0x01	/* device is open */
97#define	LPT_OBUSY	0x02	/* printer is busy doing output */
98#define	LPT_INIT	0x04	/* waiting to initialize for open */
99	u_char		sc_flags;
100#define	LPT_AUTOLF	0x20	/* automatic LF on CR XXX: LWP - not yet... */
101#define	LPT_NOINTR	0x40	/* do not use interrupt */
102};
103
104#define	LPTUNIT(s)	(minor(s) & 0x1f)
105#define	LPTFLAGS(s)	(minor(s) & 0xe0)
106#define	NOT_READY()	(MFP->mf_gpip & IO_PBSY)
107
108/* {b,c}devsw[] function prototypes */
109dev_type_open(lptopen);
110dev_type_close(lptclose);
111dev_type_write(lptwrite);
112dev_type_ioctl(lptioctl);
113
114static void lptwakeup __P((void *arg));
115static int pushbytes __P((struct lpt_softc *));
116static void lptpseudointr __P((void));
117int lptintr __P((void));
118int lpthwintr __P((int));
119
120
121/*
122 * Autoconfig stuff
123 */
124static void lptattach __P((struct device *, struct device *, void *));
125static int  lptmatch __P((struct device *, void *, void *));
126
127struct cfattach lpt_ca = {
128	sizeof(struct lpt_softc), lptmatch, lptattach
129};
130
131struct cfdriver lpt_cd = {
132	NULL, "lpt", DV_DULL, NULL, 0
133};
134
135/*ARGSUSED*/
136static	int
137lptmatch(pdp, match, auxp)
138struct	device *pdp;
139void	*match, *auxp;
140{
141	struct cfdata *cfp = match;
142
143	if (!strcmp((char *)auxp, "lpt") && cfp->cf_unit == 0)
144		return (1);
145	return (0);
146}
147
148/*ARGSUSED*/
149static void
150lptattach(pdp, dp, auxp)
151struct	device *pdp, *dp;
152void	*auxp;
153{
154	struct lpt_softc *sc = (void *)dp;
155
156	sc->sc_state = 0;
157
158	ym2149_strobe(1);
159
160	printf("\n");
161}
162
163/*
164 * Reset the printer, then wait until it's selected and not busy.
165 */
166int
167lptopen(dev, flag, mode, p)
168	dev_t		dev;
169	int		flag, mode;
170	struct proc	*p;
171{
172	int			unit = LPTUNIT(dev);
173	u_char			flags = LPTFLAGS(dev);
174	struct lpt_softc	*sc;
175	int 			error;
176	int			spin;
177	int			sps;
178
179	if (unit >= lpt_cd.cd_ndevs)
180		return ENXIO;
181	sc = lpt_cd.cd_devs[unit];
182	if (!sc)
183		return ENXIO;
184
185#ifdef DIAGNOSTIC
186	if (sc->sc_state)
187		printf("%s: stat=0x%x not zero\n", sc->sc_dev.dv_xname,
188		    sc->sc_state);
189#endif
190
191	if (sc->sc_state)
192		return EBUSY;
193
194	sc->sc_state = LPT_INIT;
195	sc->sc_flags = flags;
196	lprintf("%s: open: flags=0x%x\n", sc->sc_dev.dv_xname, flags);
197
198	/* wait till ready (printer running diagnostics) */
199	for (spin = 0; NOT_READY(); spin += STEP) {
200		if (spin >= TIMEOUT) {
201			sc->sc_state = 0;
202			return EBUSY;
203		}
204
205		/* wait 1/4 second, give up if we get a signal */
206		if ((error = tsleep((caddr_t)sc, LPTPRI | PCATCH, "lptopen",
207		     STEP)) != EWOULDBLOCK) {
208			sc->sc_state = 0;
209			return error;
210		}
211	}
212
213	sc->sc_inbuf = geteblk(LPT_BSIZE);
214	sc->sc_count = 0;
215	sc->sc_state = LPT_OPEN;
216
217	if ((sc->sc_flags & LPT_NOINTR) == 0) {
218		lptwakeup(sc);
219
220		sps = splhigh();
221		MFP->mf_imrb |= IB_PBSY;
222		MFP->mf_ierb |= IB_PBSY;
223		splx(sps);
224	}
225
226	lprintf("%s: opened\n", sc->sc_dev.dv_xname);
227	return 0;
228}
229
230void
231lptwakeup(arg)
232	void *arg;
233{
234	struct lpt_softc *sc = arg;
235
236	lptpseudointr();
237
238	timeout(lptwakeup, sc, STEP);
239}
240
241/*
242 * Close the device, and free the local line buffer.
243 */
244int
245lptclose(dev, flag, mode, p)
246	dev_t		dev;
247	int		flag;
248	int		mode;
249	struct proc	*p;
250{
251	int		 unit = LPTUNIT(dev);
252	struct lpt_softc *sc = lpt_cd.cd_devs[unit];
253	int		 sps;
254
255	if (sc->sc_count)
256		(void) pushbytes(sc);
257
258	if ((sc->sc_flags & LPT_NOINTR) == 0) {
259		untimeout(lptwakeup, sc);
260
261		sps = splhigh();
262		MFP->mf_ierb &= ~IB_PBSY;
263		MFP->mf_imrb &= ~IB_PBSY;
264		splx(sps);
265	}
266
267	sc->sc_state = 0;
268	brelse(sc->sc_inbuf);
269
270	lprintf("%s: closed\n", sc->sc_dev.dv_xname);
271	return 0;
272}
273
274int
275pushbytes(sc)
276	struct lpt_softc *sc;
277{
278	int	error;
279
280	if (sc->sc_flags & LPT_NOINTR) {
281		int spin, tic;
282
283		while (sc->sc_count > 0) {
284			spin = 0;
285			while (NOT_READY()) {
286				if (++spin < sc->sc_spinmax)
287					continue;
288				tic = 0;
289				/* adapt busy-wait algorithm */
290				sc->sc_spinmax++;
291				while (NOT_READY()) {
292					/* exponential backoff */
293					tic = tic + tic + 1;
294					if (tic > TIMEOUT)
295						tic = TIMEOUT;
296					error = tsleep((caddr_t)sc,
297					    LPTPRI | PCATCH, "lptpsh", tic);
298					if (error != EWOULDBLOCK)
299						return error;
300				}
301				break;
302			}
303
304			ym2149_write_ioport(YM_IOB, *sc->sc_cp++);
305			ym2149_strobe(0);
306			sc->sc_count--;
307			ym2149_strobe(1);
308
309			/* adapt busy-wait algorithm */
310			if (spin*2 + 16 < sc->sc_spinmax)
311				sc->sc_spinmax--;
312		}
313	} else {
314		while (sc->sc_count > 0) {
315			/* if the printer is ready for a char, give it one */
316			if ((sc->sc_state & LPT_OBUSY) == 0) {
317				lprintf("%s: write %d\n", sc->sc_dev.dv_xname,
318				    sc->sc_count);
319				(void) lptpseudointr();
320			}
321			if ((error = tsleep((caddr_t)sc, LPTPRI | PCATCH,
322			     "lptwrite2", 0)) != 0)
323				return error;
324		}
325	}
326	return 0;
327}
328
329/*
330 * Copy a line from user space to a local buffer, then call putc to get the
331 * chars moved to the output queue.
332 */
333int
334lptwrite(dev, uio, flags)
335	dev_t		dev;
336	struct uio	*uio;
337	int		flags;
338{
339	struct lpt_softc *sc = lpt_cd.cd_devs[LPTUNIT(dev)];
340	size_t n;
341	int error = 0;
342
343	while ((n = min(LPT_BSIZE, uio->uio_resid)) > 0) {
344		uiomove(sc->sc_cp = sc->sc_inbuf->b_data, n, uio);
345		sc->sc_count = n;
346		error = pushbytes(sc);
347		if (error) {
348			/*
349			 * Return accurate residual if interrupted or timed
350			 * out.
351			 */
352			uio->uio_resid += sc->sc_count;
353			sc->sc_count = 0;
354			return error;
355		}
356	}
357	return 0;
358}
359
360/*
361 * Handle printer interrupts which occur when the printer is ready to accept
362 * another char.
363 */
364int
365lptintr()
366{
367	struct lpt_softc *sc;
368
369	sc = lpt_cd.cd_devs[0]; /* XXX: LWP - we have only one on the Atari */
370
371	/* is printer online and ready for output */
372	if (NOT_READY())
373		return 0;
374
375	if (sc->sc_count) {
376
377		/* send char */
378		ym2149_write_ioport(YM_IOB, *sc->sc_cp++);
379		ym2149_strobe(0);
380		sc->sc_count--;
381		ym2149_strobe(1);
382		sc->sc_state |= LPT_OBUSY;
383	} else
384		sc->sc_state &= ~LPT_OBUSY;
385
386	if (sc->sc_count == 0) {
387		/* none, wake up the top half to get more */
388		wakeup((caddr_t)sc);
389	}
390
391	return 1;
392}
393
394static void
395lptpseudointr()
396{
397	int	s;
398
399	s = spltty();
400	lptintr();
401	splx(s);
402}
403
404int
405lpthwintr(sr)
406int	sr;
407{
408	if (!BASEPRI(sr))
409		add_sicallback((si_farg)lptpseudointr, NULL, 0);
410	else lptpseudointr();
411	return 1;
412}
413
414int
415lptioctl(dev, cmd, data, flag, p)
416	dev_t		dev;
417	u_long		cmd;
418	caddr_t		data;
419	int		flag;
420	struct proc	*p;
421{
422	int error = 0;
423
424	switch (cmd) {
425	default:
426		error = ENODEV;
427	}
428
429	return error;
430}
431