xref: /freebsd/sys/dev/ppbus/ppbconf.c (revision 0de89efe5c443f213c7ea28773ef2dc6cf3af2ed)
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: ppbconf.c,v 1.3 1997/08/28 10:15:14 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 
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  * This function is called by ppcattach().
57  */
58 struct ppb_data *
59 ppb_alloc_bus(void)
60 {
61 	struct ppb_data *ppb;
62 	static int ppbdata_initted = 0;		/* done-init flag */
63 
64 	ppb = (struct ppb_data *) malloc(sizeof(struct ppb_data),
65 		M_TEMP, M_NOWAIT);
66 
67 	/*
68 	 * Add the new parallel port bus to the list of existing ppbus.
69 	 */
70 	if (ppb) {
71 		bzero(ppb, sizeof(struct ppb_data));
72 
73 		if (!ppbdata_initted) {		/* list not initialised */
74 		    LIST_INIT(&ppbdata);
75 		    ppbdata_initted = 1;
76 		}
77 		LIST_INSERT_HEAD(&ppbdata, ppb, ppb_chain);
78 	} else {
79 		printf("ppb_alloc_bus: cannot malloc!\n");
80 	}
81 	return(ppb);
82 }
83 
84 static char *pnp_tokens[] = {
85 	"PRINTER", "MODEM", "NET", "HDC", "PCMCIA", "MEDIA",
86 	"FDC", "PORTS", "SCANNER", "DIGICAM", "", NULL };
87 
88 static char *pnp_classes[] = {
89 	"printer", "modem", "network device",
90 	"hard disk", "PCMCIA", "multimedia device",
91 	"floppy disk", "ports", "scanner",
92 	"digital camera", "unknown device", NULL };
93 
94 /*
95  * search_token()
96  *
97  * Search the first occurence of a token within a string
98  */
99 static char *
100 search_token(char *str, int slen, char *token)
101 {
102 	char *p;
103 	int tlen, i, j;
104 
105 #define UNKNOWN_LENGTH	-1
106 
107 	if (slen == UNKNOWN_LENGTH)
108 		/* get string's length */
109 		for (slen = 0, p = str; *p != '\0'; p++)
110 			slen ++;
111 
112 	/* get token's length */
113 	for (tlen = 0, p = token; *p != '\0'; p++)
114 		tlen ++;
115 
116 	if (tlen == 0)
117 		return (str);
118 
119 	for (i = 0; i <= slen-tlen; i++) {
120 		for (j = 0; j < tlen; j++)
121 			if (str[i+j] != token[j])
122 				break;
123 		if (j == tlen)
124 			return (&str[i]);
125 	}
126 
127 	return (NULL);
128 }
129 
130 /*
131  * ppb_pnp_detect()
132  *
133  * Returns the class id. of the peripherial, -1 otherwise
134  */
135 static int
136 ppb_pnp_detect(struct ppb_data *ppb)
137 {
138 	char *token, *q, *class = 0;
139 	int i, len, error;
140 	char str[PPB_PnP_STRING_SIZE+1];
141 
142 	struct ppb_device pnpdev;	/* temporary device to perform I/O */
143 
144 	/* initialize the pnpdev structure for future use */
145 	bzero(&pnpdev, sizeof(pnpdev));
146 
147 	pnpdev.ppb = ppb;
148 
149 #ifdef PnP_DEBUG
150 	printf("ppb: <PnP> probing PnP devices on ppbus%d...\n",
151 		ppb->ppb_link->adapter_unit);
152 #endif
153 
154 	ppb_wctr(&pnpdev, nINIT | SELECTIN);
155 
156 	/* select NIBBLE_1284_REQUEST_ID mode */
157 	if ((error = nibble_1284_mode(&pnpdev, NIBBLE_1284_REQUEST_ID))) {
158 #ifdef PnP_DEBUG
159 		printf("ppb: <PnP> nibble_1284_mode()=%d\n", error);
160 #endif
161 		return (-1);
162 	}
163 
164 	len = 0;
165 	for (q = str; !(ppb_rstr(&pnpdev) & ERROR); q++) {
166 		if ((error = nibble_1284_inbyte(&pnpdev, q))) {
167 #ifdef PnP_DEBUG
168 			printf("ppb: <PnP> nibble_1284_inbyte()=%d\n", error);
169 #endif
170 			return (-1);
171 		}
172 		if (len++ >= PPB_PnP_STRING_SIZE) {
173 			printf("ppb: <PnP> not space left!\n");
174 			return (-1);
175 		}
176 	}
177 	*q = '\0';
178 
179 	nibble_1284_sync(&pnpdev);
180 
181 #ifdef PnP_DEBUG
182 	printf("ppb: <PnP> %d characters: ", len);
183 	for (i = 0; i < len; i++)
184 		printf("0x%x ", str[i]);
185 	printf("\n");
186 #endif
187 
188 	/* replace ';' characters by '\0' */
189 	for (i = 0; i < len; i++)
190 		str[i] = (str[i] == ';') ? '\0' : str[i];
191 
192 	if ((token = search_token(str, len, "MFG")) != NULL)
193 		printf("ppbus%d: <%s", ppb->ppb_link->adapter_unit,
194 			search_token(token, UNKNOWN_LENGTH, ":") + 1);
195 	else
196 		printf("ppbus%d: <unknown", ppb->ppb_link->adapter_unit);
197 
198 	if ((token = search_token(str, len, "MDL")) != NULL)
199 		printf(" %s",
200 			search_token(token, UNKNOWN_LENGTH, ":") + 1);
201 	else
202 		printf(" unknown");
203 
204 	if ((token = search_token(str, len, "VER")) != NULL)
205 		printf("/%s",
206 			search_token(token, UNKNOWN_LENGTH, ":") + 1);
207 
208 	if ((token = search_token(str, len, "REV")) != NULL)
209 		printf(".%s",
210 			search_token(token, UNKNOWN_LENGTH, ":") + 1);
211 
212 	printf(">");
213 
214 	if ((token = search_token(str, len, "CLS")) != NULL) {
215 		class = search_token(token, UNKNOWN_LENGTH, ":") + 1;
216 		printf(" %s", class);
217 	}
218 
219 	if ((token = search_token(str, len, "CMD")) != NULL)
220 		printf(" %s",
221 			search_token(token, UNKNOWN_LENGTH, ":") + 1);
222 
223 	printf("\n");
224 
225 	if (class)
226 		/* identify class ident */
227 		for (i = 0; pnp_tokens[i] != NULL; i++) {
228 			if (search_token(class, len, pnp_tokens[i]) != NULL) {
229 				return (i);
230 				break;
231 			}
232 		}
233 
234 	return (PPB_PnP_UNKNOWN);
235 }
236 
237 /*
238  * ppb_attachdevs()
239  *
240  * Called by ppcattach(), this function probes the ppbus and
241  * attaches found devices.
242  */
243 int
244 ppb_attachdevs(struct ppb_data *ppb)
245 {
246 	int error;
247 	struct ppb_device *dev;
248 	struct ppb_driver **p_drvpp, *p_drvp;
249 
250 	LIST_INIT(&ppb->ppb_devs);	/* initialise device/driver list */
251 	p_drvpp = (struct ppb_driver **)ppbdriver_set.ls_items;
252 
253 	/* detect PnP devices */
254 	ppb->class_id = ppb_pnp_detect(ppb);
255 
256 	/*
257 	 * Blindly try all probes here.  Later we should look at
258 	 * the parallel-port PnP standard, and intelligently seek
259 	 * drivers based on configuration first.
260 	 */
261 	while ((p_drvp = *p_drvpp++) != NULL) {
262 	    if (p_drvp->probe && (dev = (p_drvp->probe(ppb))) != NULL) {
263 		/*
264 		 * Add the device to the list of probed devices.
265 		 */
266 		LIST_INSERT_HEAD(&ppb->ppb_devs, dev, chain);
267 
268 		/* Call the device's attach routine */
269 		(void)p_drvp->attach(dev);
270 	    }
271 	}
272 	return (0);
273 }
274 
275 /*
276  * ppb_next_bus()
277  *
278  * Return the next bus in ppbus queue
279  */
280 struct ppb_data *
281 ppb_next_bus(struct ppb_data *ppb)
282 {
283 
284 	if (ppb == NULL)
285 		return (ppbdata.lh_first);
286 
287 	return (ppb->ppb_chain.le_next);
288 }
289 
290 /*
291  * ppb_lookup_bus()
292  *
293  * Get ppb_data structure pointer according to the base address of the ppbus
294  */
295 struct ppb_data *
296 ppb_lookup_bus(int base_port)
297 {
298 	struct ppb_data *ppb;
299 
300 	for (ppb = ppbdata.lh_first; ppb; ppb = ppb->ppb_chain.le_next)
301 		if (ppb->ppb_link->base == base_port)
302 			break;
303 
304 	return (ppb);
305 }
306 
307 /*
308  * ppb_attach_device()
309  *
310  * Called by loadable kernel modules to add a device
311  */
312 int
313 ppb_attach_device(struct ppb_device *dev)
314 {
315 	struct ppb_data *ppb = dev->ppb;
316 
317 	/* add the device to the list of probed devices */
318 	LIST_INSERT_HEAD(&ppb->ppb_devs, dev, chain);
319 
320 	return (0);
321 }
322 
323 /*
324  * ppb_remove_device()
325  *
326  * Called by loadable kernel modules to remove a device
327  */
328 void
329 ppb_remove_device(struct ppb_device *dev)
330 {
331 
332 	/* remove the device from the list of probed devices */
333 	LIST_REMOVE(dev, chain);
334 
335 	return;
336 }
337 
338 /*
339  * ppb_request_bus()
340  *
341  * Allocate the device to perform transfers.
342  *
343  * how	: PPB_WAIT or PPB_DONTWAIT
344  */
345 int
346 ppb_request_bus(struct ppb_device *dev, int how)
347 {
348 	int s, error = 0;
349 	struct ppb_data *ppb = dev->ppb;
350 
351 	while (!error) {
352 		s = splhigh();
353 		if (ppb->ppb_owner) {
354 			splx(s);
355 
356 			switch (how) {
357 			case (PPB_WAIT | PPB_INTR):
358 				error = tsleep(ppb, PPBPRI|PCATCH, "ppbreq", 0);
359 				break;
360 
361 			case (PPB_WAIT | PPB_NOINTR):
362 				error = tsleep(ppb, PPBPRI, "ppbreq", 0);
363 				break;
364 
365 			default:
366 				return (EWOULDBLOCK);
367 				break;
368 			}
369 
370 		} else {
371 			ppb->ppb_owner = dev;
372 
373 			splx(s);
374 			return (0);
375 		}
376 	}
377 
378 	return (error);
379 }
380 
381 /*
382  * ppb_release_bus()
383  *
384  * Release the device allocated with ppb_request_dev()
385  */
386 int
387 ppb_release_bus(struct ppb_device *dev)
388 {
389 	int s;
390 	struct ppb_data *ppb = dev->ppb;
391 
392 	s = splhigh();
393 	if (ppb->ppb_owner != dev) {
394 		splx(s);
395 		return (EACCES);
396 	}
397 
398 	ppb->ppb_owner = 0;
399 	splx(s);
400 
401 	/* wakeup waiting processes */
402 	wakeup(ppb);
403 
404 	return (0);
405 }
406