ppbconf.c revision 39134
1/*-
2 * Copyright (c) 1997, 1998 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: ppbconf.c,v 1.6 1998/08/03 19:14:31 msmith Exp $
27 *
28 */
29#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/kernel.h>
32#include <sys/malloc.h>
33
34#include <vm/vm.h>
35#include <vm/pmap.h>
36
37#include <dev/ppbus/ppbconf.h>
38#include <dev/ppbus/ppb_1284.h>
39
40LIST_HEAD(, ppb_data)	ppbdata;	/* list of existing ppbus */
41
42/*
43 * Add a null driver so that the linker set always exists.
44 */
45
46static struct ppb_driver nulldriver = {
47    NULL, NULL, "null"
48};
49DATA_SET(ppbdriver_set, nulldriver);
50
51
52/*
53 * ppb_alloc_bus()
54 *
55 * Allocate area to store the ppbus description.
56 */
57struct ppb_data *
58ppb_alloc_bus(void)
59{
60	struct ppb_data *ppb;
61	static int ppbdata_initted = 0;		/* done-init flag */
62
63	ppb = (struct ppb_data *) malloc(sizeof(struct ppb_data),
64		M_TEMP, M_NOWAIT);
65
66	/*
67	 * Add the new parallel port bus to the list of existing ppbus.
68	 */
69	if (ppb) {
70		bzero(ppb, sizeof(struct ppb_data));
71
72		if (!ppbdata_initted) {		/* list not initialised */
73		    LIST_INIT(&ppbdata);
74		    ppbdata_initted = 1;
75		}
76		LIST_INSERT_HEAD(&ppbdata, ppb, ppb_chain);
77	} else {
78		printf("ppb_alloc_bus: cannot malloc!\n");
79	}
80	return(ppb);
81}
82
83static char *pnp_tokens[] = {
84	"PRINTER", "MODEM", "NET", "HDC", "PCMCIA", "MEDIA",
85	"FDC", "PORTS", "SCANNER", "DIGICAM", "", NULL };
86
87static char *pnp_classes[] = {
88	"printer", "modem", "network device",
89	"hard disk", "PCMCIA", "multimedia device",
90	"floppy disk", "ports", "scanner",
91	"digital camera", "unknown device", NULL };
92
93/*
94 * search_token()
95 *
96 * Search the first occurence of a token within a string
97 *
98 * XXX should use strxxx() calls
99 */
100static char *
101search_token(char *str, int slen, char *token)
102{
103	char *p;
104	int tlen, i, j;
105
106#define UNKNOWN_LENGTH	-1
107
108	if (slen == UNKNOWN_LENGTH)
109		/* get string's length */
110		for (slen = 0, p = str; *p != '\0'; p++)
111			slen ++;
112
113	/* get token's length */
114	for (tlen = 0, p = token; *p != '\0'; p++)
115		tlen ++;
116
117	if (tlen == 0)
118		return (str);
119
120	for (i = 0; i <= slen-tlen; i++) {
121		for (j = 0; j < tlen; j++)
122			if (str[i+j] != token[j])
123				break;
124		if (j == tlen)
125			return (&str[i]);
126	}
127
128	return (NULL);
129}
130
131/*
132 * ppb_pnp_detect()
133 *
134 * Returns the class id. of the peripherial, -1 otherwise
135 */
136static int
137ppb_pnp_detect(struct ppb_data *ppb)
138{
139	char *token, *q, *class = 0;
140	int i, len, error;
141	int class_id = -1;
142	char str[PPB_PnP_STRING_SIZE+1];
143	struct ppb_device pnpdev;	/* temporary device to perform I/O */
144
145	/* initialize the pnpdev structure for future use */
146	bzero(&pnpdev, sizeof(pnpdev));
147
148	pnpdev.ppb = ppb;
149
150	if (bootverbose)
151		printf("ppb: <PnP> probing devices on ppbus %d...\n",
152			ppb->ppb_link->adapter_unit);
153
154	if (ppb_request_bus(&pnpdev, PPB_DONTWAIT)) {
155		if (bootverbose)
156			printf("ppb: <PnP> cannot allocate ppbus!\n");
157		return (-1);
158	}
159
160	if ((error = ppb_1284_negociate(&pnpdev, NIBBLE_1284_REQUEST_ID))) {
161		if (bootverbose)
162			printf("ppb: <PnP> ppb_1284_negociate()=%d\n", error);
163
164		goto end_detect;
165	}
166
167	len = 0;
168	for (q=str; !(ppb_rstr(&pnpdev) & PERROR); q++) {
169		if ((error = nibble_1284_inbyte(&pnpdev, q))) {
170			if (bootverbose) {
171				*q = '\0';
172				printf("ppb: <PnP> len=%d, %s\n", len, str);
173				printf("ppb: <PnP> nibble_1284_inbyte()=%d\n",
174					error);
175			}
176			goto end_detect;
177		}
178
179		if (len++ >= PPB_PnP_STRING_SIZE) {
180			printf("ppb: <PnP> not space left!\n");
181			goto end_detect;
182		}
183	}
184	*q = '\0';
185
186	nibble_1284_sync(&pnpdev);
187
188	if (bootverbose) {
189		printf("ppb: <PnP> %d characters: ", len);
190		for (i = 0; i < len; i++)
191			printf("0x%x ", str[i]);
192		printf("\n");
193	}
194
195	/* replace ';' characters by '\0' */
196	for (i = 0; i < len; i++)
197		str[i] = (str[i] == ';') ? '\0' : str[i];
198
199	if ((token = search_token(str, len, "MFG")) != NULL)
200		printf("ppbus%d: <%s", ppb->ppb_link->adapter_unit,
201			search_token(token, UNKNOWN_LENGTH, ":") + 1);
202	else
203		printf("ppbus%d: <unknown", ppb->ppb_link->adapter_unit);
204
205	if ((token = search_token(str, len, "MDL")) != NULL)
206		printf(" %s",
207			search_token(token, UNKNOWN_LENGTH, ":") + 1);
208	else
209		printf(" unknown");
210
211	if ((token = search_token(str, len, "VER")) != NULL)
212		printf("/%s",
213			search_token(token, UNKNOWN_LENGTH, ":") + 1);
214
215	if ((token = search_token(str, len, "REV")) != NULL)
216		printf(".%s",
217			search_token(token, UNKNOWN_LENGTH, ":") + 1);
218
219	printf(">");
220
221	if ((token = search_token(str, len, "CLS")) != NULL) {
222		class = search_token(token, UNKNOWN_LENGTH, ":") + 1;
223		printf(" %s", class);
224	}
225
226	if ((token = search_token(str, len, "CMD")) != NULL)
227		printf(" %s",
228			search_token(token, UNKNOWN_LENGTH, ":") + 1);
229
230	printf("\n");
231
232	if (class)
233		/* identify class ident */
234		for (i = 0; pnp_tokens[i] != NULL; i++) {
235			if (search_token(class, len, pnp_tokens[i]) != NULL) {
236				class_id = i;
237				goto end_detect;
238			}
239		}
240
241	class_id = PPB_PnP_UNKNOWN;
242
243end_detect:
244	if ((error = ppb_1284_terminate(&pnpdev, VALID_STATE)) && bootverbose)
245		printf("ppb: ppb_1284_terminate()=%d\n", error);
246
247	ppb_release_bus(&pnpdev);
248	return (class_id);
249}
250
251/*
252 * ppb_attachdevs()
253 *
254 * Called by ppcattach(), this function probes the ppbus and
255 * attaches found devices.
256 */
257int
258ppb_attachdevs(struct ppb_data *ppb)
259{
260	int error;
261	struct ppb_device *dev;
262	struct ppb_driver **p_drvpp, *p_drvp;
263
264	LIST_INIT(&ppb->ppb_devs);	/* initialise device/driver list */
265	p_drvpp = (struct ppb_driver **)ppbdriver_set.ls_items;
266
267	/* detect PnP devices */
268	ppb->class_id = ppb_pnp_detect(ppb);
269
270	/*
271	 * Blindly try all probes here.  Later we should look at
272	 * the parallel-port PnP standard, and intelligently seek
273	 * drivers based on configuration first.
274	 */
275	while ((p_drvp = *p_drvpp++) != NULL) {
276	    if (p_drvp->probe && (dev = (p_drvp->probe(ppb))) != NULL) {
277		/*
278		 * Add the device to the list of probed devices.
279		 */
280		LIST_INSERT_HEAD(&ppb->ppb_devs, dev, chain);
281
282		/* Call the device's attach routine */
283		(void)p_drvp->attach(dev);
284	    }
285	}
286	return (0);
287}
288
289/*
290 * ppb_next_bus()
291 *
292 * Return the next bus in ppbus queue
293 */
294struct ppb_data *
295ppb_next_bus(struct ppb_data *ppb)
296{
297
298	if (ppb == NULL)
299		return (ppbdata.lh_first);
300
301	return (ppb->ppb_chain.le_next);
302}
303
304/*
305 * ppb_lookup_bus()
306 *
307 * Get ppb_data structure pointer according to the base address of the ppbus
308 */
309struct ppb_data *
310ppb_lookup_bus(int base_port)
311{
312	struct ppb_data *ppb;
313
314	for (ppb = ppbdata.lh_first; ppb; ppb = ppb->ppb_chain.le_next)
315		if (ppb->ppb_link->base == base_port)
316			break;
317
318	return (ppb);
319}
320
321/*
322 * ppb_lookup_link()
323 *
324 * Get ppb_data structure pointer according to the unit value
325 * of the corresponding link structure
326 */
327struct ppb_data *
328ppb_lookup_link(int unit)
329{
330	struct ppb_data *ppb;
331
332	for (ppb = ppbdata.lh_first; ppb; ppb = ppb->ppb_chain.le_next)
333		if (ppb->ppb_link->adapter_unit == unit)
334			break;
335
336	return (ppb);
337}
338
339/*
340 * ppb_attach_device()
341 *
342 * Called by loadable kernel modules to add a device
343 */
344int
345ppb_attach_device(struct ppb_device *dev)
346{
347	struct ppb_data *ppb = dev->ppb;
348
349	/* add the device to the list of probed devices */
350	LIST_INSERT_HEAD(&ppb->ppb_devs, dev, chain);
351
352	return (0);
353}
354
355/*
356 * ppb_remove_device()
357 *
358 * Called by loadable kernel modules to remove a device
359 */
360void
361ppb_remove_device(struct ppb_device *dev)
362{
363
364	/* remove the device from the list of probed devices */
365	LIST_REMOVE(dev, chain);
366
367	return;
368}
369
370/*
371 * ppb_request_bus()
372 *
373 * Allocate the device to perform transfers.
374 *
375 * how	: PPB_WAIT or PPB_DONTWAIT
376 */
377int
378ppb_request_bus(struct ppb_device *dev, int how)
379{
380	int s, error = 0;
381	struct ppb_data *ppb = dev->ppb;
382
383	while (!error) {
384		s = splhigh();
385		if (ppb->ppb_owner) {
386			splx(s);
387
388			switch (how) {
389			case (PPB_WAIT | PPB_INTR):
390				error = tsleep(ppb, PPBPRI|PCATCH, "ppbreq", 0);
391				break;
392
393			case (PPB_WAIT | PPB_NOINTR):
394				error = tsleep(ppb, PPBPRI, "ppbreq", 0);
395				break;
396
397			default:
398				return (EWOULDBLOCK);
399				break;
400			}
401
402		} else {
403			ppb->ppb_owner = dev;
404
405			/* restore the context of the device
406			 * The first time, ctx.valid is certainly false
407			 * then do not change anything. This is usefull for
408			 * drivers that do not set there operating mode
409			 * during attachement
410			 */
411			if (dev->ctx.valid)
412				ppb_set_mode(dev, dev->ctx.mode);
413
414			splx(s);
415			return (0);
416		}
417	}
418
419	return (error);
420}
421
422/*
423 * ppb_release_bus()
424 *
425 * Release the device allocated with ppb_request_dev()
426 */
427int
428ppb_release_bus(struct ppb_device *dev)
429{
430	int s;
431	struct ppb_data *ppb = dev->ppb;
432
433	s = splhigh();
434	if (ppb->ppb_owner != dev) {
435		splx(s);
436		return (EACCES);
437	}
438
439	ppb->ppb_owner = 0;
440	splx(s);
441
442	/* save the context of the device */
443	dev->ctx.mode = ppb_get_mode(dev);
444
445	/* ok, now the context of the device is valid */
446	dev->ctx.valid = 1;
447
448	/* wakeup waiting processes */
449	wakeup(ppb);
450
451	return (0);
452}
453