xref: /freebsd/sys/dev/ppbus/ppbconf.c (revision 0640d357f29fb1c0daaaffadd0416c5981413afd)
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.7 1998/09/13 18:26:26 nsouch 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 
40 LIST_HEAD(, ppb_data)	ppbdata;	/* list of existing ppbus */
41 
42 /*
43  * Add a null driver so that the linker set always exists.
44  */
45 
46 static struct ppb_driver nulldriver = {
47     NULL, NULL, "null"
48 };
49 DATA_SET(ppbdriver_set, nulldriver);
50 
51 
52 /*
53  * ppb_alloc_bus()
54  *
55  * Allocate area to store the ppbus description.
56  */
57 struct ppb_data *
58 ppb_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 
83 static char *pnp_tokens[] = {
84 	"PRINTER", "MODEM", "NET", "HDC", "PCMCIA", "MEDIA",
85 	"FDC", "PORTS", "SCANNER", "DIGICAM", "", NULL };
86 
87 static 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  */
100 static char *
101 search_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  */
136 static int
137 ppb_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 
243 end_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  */
257 int
258 ppb_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 /* XXX wait for ieee1284 good support */
268 #if 0
269 	/* detect PnP devices */
270 	ppb->class_id = ppb_pnp_detect(ppb);
271 #endif
272 
273 	/*
274 	 * Blindly try all probes here.  Later we should look at
275 	 * the parallel-port PnP standard, and intelligently seek
276 	 * drivers based on configuration first.
277 	 */
278 	while ((p_drvp = *p_drvpp++) != NULL) {
279 	    if (p_drvp->probe && (dev = (p_drvp->probe(ppb))) != NULL) {
280 		/*
281 		 * Add the device to the list of probed devices.
282 		 */
283 		LIST_INSERT_HEAD(&ppb->ppb_devs, dev, chain);
284 
285 		/* Call the device's attach routine */
286 		(void)p_drvp->attach(dev);
287 	    }
288 	}
289 	return (0);
290 }
291 
292 /*
293  * ppb_next_bus()
294  *
295  * Return the next bus in ppbus queue
296  */
297 struct ppb_data *
298 ppb_next_bus(struct ppb_data *ppb)
299 {
300 
301 	if (ppb == NULL)
302 		return (ppbdata.lh_first);
303 
304 	return (ppb->ppb_chain.le_next);
305 }
306 
307 /*
308  * ppb_lookup_bus()
309  *
310  * Get ppb_data structure pointer according to the base address of the ppbus
311  */
312 struct ppb_data *
313 ppb_lookup_bus(int base_port)
314 {
315 	struct ppb_data *ppb;
316 
317 	for (ppb = ppbdata.lh_first; ppb; ppb = ppb->ppb_chain.le_next)
318 		if (ppb->ppb_link->base == base_port)
319 			break;
320 
321 	return (ppb);
322 }
323 
324 /*
325  * ppb_lookup_link()
326  *
327  * Get ppb_data structure pointer according to the unit value
328  * of the corresponding link structure
329  */
330 struct ppb_data *
331 ppb_lookup_link(int unit)
332 {
333 	struct ppb_data *ppb;
334 
335 	for (ppb = ppbdata.lh_first; ppb; ppb = ppb->ppb_chain.le_next)
336 		if (ppb->ppb_link->adapter_unit == unit)
337 			break;
338 
339 	return (ppb);
340 }
341 
342 /*
343  * ppb_attach_device()
344  *
345  * Called by loadable kernel modules to add a device
346  */
347 int
348 ppb_attach_device(struct ppb_device *dev)
349 {
350 	struct ppb_data *ppb = dev->ppb;
351 
352 	/* add the device to the list of probed devices */
353 	LIST_INSERT_HEAD(&ppb->ppb_devs, dev, chain);
354 
355 	return (0);
356 }
357 
358 /*
359  * ppb_remove_device()
360  *
361  * Called by loadable kernel modules to remove a device
362  */
363 void
364 ppb_remove_device(struct ppb_device *dev)
365 {
366 
367 	/* remove the device from the list of probed devices */
368 	LIST_REMOVE(dev, chain);
369 
370 	return;
371 }
372 
373 /*
374  * ppb_request_bus()
375  *
376  * Allocate the device to perform transfers.
377  *
378  * how	: PPB_WAIT or PPB_DONTWAIT
379  */
380 int
381 ppb_request_bus(struct ppb_device *dev, int how)
382 {
383 	int s, error = 0;
384 	struct ppb_data *ppb = dev->ppb;
385 
386 	while (!error) {
387 		s = splhigh();
388 		if (ppb->ppb_owner) {
389 			splx(s);
390 
391 			switch (how) {
392 			case (PPB_WAIT | PPB_INTR):
393 				error = tsleep(ppb, PPBPRI|PCATCH, "ppbreq", 0);
394 				break;
395 
396 			case (PPB_WAIT | PPB_NOINTR):
397 				error = tsleep(ppb, PPBPRI, "ppbreq", 0);
398 				break;
399 
400 			default:
401 				return (EWOULDBLOCK);
402 				break;
403 			}
404 
405 		} else {
406 			ppb->ppb_owner = dev;
407 
408 			/* restore the context of the device
409 			 * The first time, ctx.valid is certainly false
410 			 * then do not change anything. This is usefull for
411 			 * drivers that do not set there operating mode
412 			 * during attachement
413 			 */
414 			if (dev->ctx.valid)
415 				ppb_set_mode(dev, dev->ctx.mode);
416 
417 			splx(s);
418 			return (0);
419 		}
420 	}
421 
422 	return (error);
423 }
424 
425 /*
426  * ppb_release_bus()
427  *
428  * Release the device allocated with ppb_request_dev()
429  */
430 int
431 ppb_release_bus(struct ppb_device *dev)
432 {
433 	int s;
434 	struct ppb_data *ppb = dev->ppb;
435 
436 	s = splhigh();
437 	if (ppb->ppb_owner != dev) {
438 		splx(s);
439 		return (EACCES);
440 	}
441 
442 	ppb->ppb_owner = 0;
443 	splx(s);
444 
445 	/* save the context of the device */
446 	dev->ctx.mode = ppb_get_mode(dev);
447 
448 	/* ok, now the context of the device is valid */
449 	dev->ctx.valid = 1;
450 
451 	/* wakeup waiting processes */
452 	wakeup(ppb);
453 
454 	return (0);
455 }
456