ppbconf.c revision 28219
1/*-
2 * Copyright (c) 1997 Nicolas Souchu
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 *	$Id$
27 *
28 */
29#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/errno.h>
32#include <sys/conf.h>
33#include <sys/proc.h>
34#include <sys/buf.h>
35#include <sys/kernel.h>
36#include <sys/malloc.h>
37#include <sys/uio.h>
38#include <sys/syslog.h>
39
40#include <machine/clock.h>
41#include <machine/lpt.h>
42
43#include <vm/vm.h>
44#include <vm/vm_param.h>
45#include <vm/pmap.h>
46
47#include <i386/isa/isa.h>
48#include <i386/isa/isa_device.h>
49
50#include <dev/ppbus/ppbconf.h>
51
52LIST_HEAD(, ppb_data)	ppbdata;	/* list of existing ppbus */
53
54/*
55 * Add a null driver so that the linker set always exists.
56 */
57
58static struct ppb_driver nulldriver = {
59    NULL, NULL, "null"
60};
61DATA_SET(ppbdriver_set, nulldriver);
62
63
64/*
65 * Parallel Port Bus sleep/wakeup queue.
66 */
67#define PRIPPB	28	/* PSOCK < PRIPPB < PWAIT 		XXX */
68
69/*
70 * ppb_alloc_bus()
71 *
72 * Allocate area to store the ppbus description.
73 * This function is called by ppcattach().
74 */
75struct ppb_data *
76ppb_alloc_bus(void)
77{
78	struct ppb_data *ppb;
79	static int ppbdata_initted = 0;		/* done-init flag */
80
81	ppb = (struct ppb_data *) malloc(sizeof(struct ppb_data),
82		M_TEMP, M_NOWAIT);
83
84	/*
85	 * Add the new parallel port bus to the list of existing ppbus.
86	 */
87	if (ppb) {
88		bzero(ppb, sizeof(struct ppb_data));
89
90		if (!ppbdata_initted) {		/* list not initialised */
91		    LIST_INIT(&ppbdata);
92		    ppbdata_initted = 1;
93		}
94		LIST_INSERT_HEAD(&ppbdata, ppb, ppb_chain);
95	} else {
96		printf("ppb_alloc_bus: cannot malloc!\n");
97	}
98	return(ppb);
99}
100
101/*
102 * ppb_attachdevs()
103 *
104 * Called by ppcattach(), this function probes the ppbus and
105 * attaches found devices.
106 */
107int
108ppb_attachdevs(struct ppb_data *ppb)
109{
110	int error;
111	struct ppb_device *dev;
112	struct ppb_driver **p_drvpp, *p_drvp;
113
114	LIST_INIT(&ppb->ppb_devs);	/* initialise device/driver list */
115	p_drvpp = (struct ppb_driver **)ppbdriver_set.ls_items;
116
117	/*
118	 * Blindly try all probes here.  Later we should look at
119	 * the parallel-port PnP standard, and intelligently seek
120	 * drivers based on configuration first.
121	 */
122	while ((p_drvp = *p_drvpp++) != NULL) {
123	    if (p_drvp->probe && (dev = (p_drvp->probe(ppb))) != NULL) {
124		/*
125		 * Add the device to the list of probed devices.
126		 */
127		LIST_INSERT_HEAD(&ppb->ppb_devs, dev, chain);
128
129		/* Call the device's attach routine */
130		(void)p_drvp->attach(dev);
131	    }
132	}
133	return (0);
134}
135
136/*
137 * ppb_request_bus()
138 *
139 * Allocate the device to perform transfers.
140 *
141 * how	: PPB_WAIT or PPB_DONTWAIT
142 */
143int
144ppb_request_bus(struct ppb_device *dev, int how)
145{
146	int s, error = 0;
147	struct ppb_data *ppb = dev->ppb;
148
149	/*
150	 * During initialisation, ppb is null.
151	 */
152	if (!ppb)
153		return (0);
154
155	while (error != EINTR) {
156		s = splhigh();
157		if (ppb->ppb_owner) {
158			splx(s);
159
160			switch (how) {
161			case (PPB_WAIT | PPB_INTR):
162
163				error = tsleep(ppb, PRIPPB | PCATCH,
164						"ppbreq", 0);
165				break;
166			case (PPB_WAIT):
167				error = tsleep(ppb, PRIPPB, "ppbreq", 0);
168				break;
169			default:
170				return EWOULDBLOCK;
171				break;
172			}
173
174		} else {
175			ppb->ppb_owner = dev;
176
177			splx(s);
178			return (0);
179		}
180	}
181
182	return (EINTR);
183}
184
185/*
186 * ppb_release_bus()
187 *
188 * Release the device allocated with ppb_request_dev()
189 */
190int
191ppb_release_bus(struct ppb_device *dev)
192{
193	int s;
194	struct ppb_data *ppb = dev->ppb;
195
196	/*
197	 * During initialisation, ppb is null.
198	 */
199	if (!ppb)
200		return (0);
201
202	s = splhigh();
203	if (ppb->ppb_owner != dev) {
204		splx(s);
205		return (EACCES);
206	}
207
208	ppb->ppb_owner = 0;
209	splx(s);
210
211	/*
212	 * Wakeup waiting processes.
213	 */
214	wakeup(ppb);
215
216	return (0);
217}
218
219/*
220 * ppb_intr()
221 *
222 * Function called by ppcintr() when an intr occurs.
223 */
224void
225ppb_intr(struct ppb_link *pl)
226{
227	struct ppb_data *ppb = pl->ppbus;
228
229	/*
230	 * Call chipset dependent code.
231	 * Should be filled at chipset initialisation if needed.
232	 */
233	if (pl->adapter->intr_handler)
234		(*pl->adapter->intr_handler)(pl->adapter_unit);
235
236	/*
237	 * Call upper handler iff the bus is owned by a device and
238	 * this device has specified an interrupt handler.
239	 */
240	if (ppb->ppb_owner && ppb->ppb_owner->intr)
241		(*ppb->ppb_owner->intr)(ppb->ppb_owner->id_unit);
242
243	return;
244}
245
246/*
247 * ppb_reset_epp_timeout()
248 *
249 * Reset the EPP timeout bit in the status register.
250 */
251int
252ppb_reset_epp_timeout(struct ppb_device *dev)
253{
254	struct ppb_data *ppb = dev->ppb;
255
256	if (ppb->ppb_owner != dev)
257		return (EACCES);
258
259	(*ppb->ppb_link->adapter->reset_epp_timeout)(dev->id_unit);
260
261	return (0);
262}
263
264/*
265 * ppb_ecp_sync()
266 *
267 * Wait for the ECP FIFO to be empty.
268 */
269int
270ppb_ecp_sync(struct ppb_device *dev)
271{
272	struct ppb_data *ppb = dev->ppb;
273
274	if (ppb->ppb_owner != dev)
275		return (EACCES);
276
277	(*ppb->ppb_link->adapter->ecp_sync)(dev->id_unit);
278
279	return (0);
280}
281
282/*
283 * ppb_get_mode()
284 *
285 * Read the mode (SPP, EPP...) of the chipset.
286 */
287int
288ppb_get_mode(struct ppb_device *dev)
289{
290	return (dev->ppb->ppb_link->mode);
291}
292
293/*
294 * ppb_get_epp_protocol()
295 *
296 * Read the EPP protocol (1.9 or 1.7).
297 */
298int
299ppb_get_epp_protocol(struct ppb_device *dev)
300{
301	return (dev->ppb->ppb_link->epp_protocol);
302}
303
304/*
305 * ppb_get_irq()
306 *
307 * Return the irq, 0 if none.
308 */
309int
310ppb_get_irq(struct ppb_device *dev)
311{
312	return (dev->ppb->ppb_link->id_irq);
313}
314
315/*
316 * ppb_get_status()
317 *
318 * Read the status register and update the status info.
319 */
320int
321ppb_get_status(struct ppb_device *dev, struct ppb_status *status)
322{
323	struct ppb_data *ppb = dev->ppb;
324	register char r;
325
326	if (ppb->ppb_owner != dev)
327		return (EACCES);
328
329	r = status->status = ppb_rstr(dev);
330
331	status->timeout	= r & TIMEOUT;
332	status->error	= !(r & nFAULT);
333	status->select	= r & SELECT;
334	status->paper_end = r & ERROR;
335	status->ack	= !(r & nACK);
336	status->busy	= !(r & nBUSY);
337
338	return (0);
339}
340