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