ppbconf.c revision 33181
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.4 1997/09/01 00:51:46 bde 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
40static 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
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 * This function is called by ppcattach().
57 */
58struct ppb_data *
59ppb_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
84static char *pnp_tokens[] = {
85	"PRINTER", "MODEM", "NET", "HDC", "PCMCIA", "MEDIA",
86	"FDC", "PORTS", "SCANNER", "DIGICAM", "", NULL };
87
88static 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 */
99static char *
100search_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 */
135static int
136ppb_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 */
243int
244ppb_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 */
280struct ppb_data *
281ppb_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 */
295struct ppb_data *
296ppb_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 */
312int
313ppb_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 */
328void
329ppb_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 */
345int
346ppb_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 */
386int
387ppb_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