ppbconf.c revision 55939
1/*-
2 * Copyright (c) 1997, 1998, 1999 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 * $FreeBSD: head/sys/dev/ppbus/ppbconf.c 55939 2000-01-14 00:18:06Z nsouch $
27 *
28 */
29#include "opt_ppb_1284.h"
30
31#include <sys/param.h>
32#include <sys/systm.h>
33#include <sys/kernel.h>
34#include <sys/module.h>
35#include <sys/bus.h>
36#include <sys/malloc.h>
37
38#include <dev/ppbus/ppbconf.h>
39#include <dev/ppbus/ppb_1284.h>
40
41#include "ppbus_if.h"
42
43#define DEVTOSOFTC(dev) ((struct ppb_data *)device_get_softc(dev))
44
45MALLOC_DEFINE(M_PPBUSDEV, "ppbusdev", "Parallel Port bus device");
46
47static devclass_t ppbus_devclass;
48
49/*
50 * Device methods
51 */
52static int ppbus_probe(device_t);
53static int ppbus_attach(device_t);
54static void ppbus_print_child(device_t bus, device_t dev);
55static int ppbus_read_ivar(device_t, device_t, int, uintptr_t *);
56static int ppbus_write_ivar(device_t, device_t, int, u_long);
57static int ppbus_setup_intr(device_t, device_t, struct resource *, int,
58		void (*)(void *), void *, void **);
59static int ppbus_teardown_intr(device_t, device_t, struct resource *, void *);
60
61static device_method_t ppbus_methods[] = {
62        /* device interface */
63        DEVMETHOD(device_probe,         ppbus_probe),
64        DEVMETHOD(device_attach,        ppbus_attach),
65
66        /* bus interface */
67	DEVMETHOD(bus_print_child,	ppbus_print_child),
68        DEVMETHOD(bus_read_ivar,        ppbus_read_ivar),
69        DEVMETHOD(bus_write_ivar,       ppbus_write_ivar),
70	DEVMETHOD(bus_setup_intr,	ppbus_setup_intr),
71	DEVMETHOD(bus_teardown_intr,	ppbus_teardown_intr),
72	DEVMETHOD(bus_alloc_resource,   bus_generic_alloc_resource),
73
74        { 0, 0 }
75};
76
77static driver_t ppbus_driver = {
78        "ppbus",
79        ppbus_methods,
80        sizeof(struct ppb_data),
81  };
82
83static void
84ppbus_print_child(device_t bus, device_t dev)
85{
86	struct ppb_device *ppbdev;
87
88	bus_print_child_header(bus, dev);
89
90	ppbdev = (struct ppb_device *)device_get_ivars(dev);
91
92	if (ppbdev->flags != 0)
93		printf(" flags 0x%x", ppbdev->flags);
94
95	printf(" on %s%d\n", device_get_name(bus), device_get_unit(bus));
96
97	return;
98}
99
100static int
101ppbus_probe(device_t dev)
102{
103	device_set_desc(dev, "Parallel port bus");
104
105	return (0);
106}
107
108/*
109 * ppb_add_device()
110 *
111 * Add a ppbus device, allocate/initialize the ivars
112 */
113static void
114ppbus_add_device(device_t dev, const char *name, int unit)
115{
116	struct ppb_device *ppbdev;
117	device_t child;
118
119	/* allocate ivars for the new ppbus child */
120	ppbdev = malloc(sizeof(struct ppb_device), M_PPBUSDEV, M_NOWAIT);
121	if (!ppbdev)
122		return;
123	bzero(ppbdev, sizeof *ppbdev);
124
125	/* initialize the ivars */
126	ppbdev->name = name;
127
128	/* add the device as a child to the ppbus bus with the allocated
129	 * ivars */
130	child = device_add_child(dev, name, unit);
131	device_set_ivars(child, ppbdev);
132
133	return;
134}
135
136static int
137ppbus_read_ivar(device_t bus, device_t dev, int index, uintptr_t* val)
138  {
139	struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(dev);
140
141	switch (index) {
142	case PPBUS_IVAR_MODE:
143		/* XXX yet device mode = ppbus mode = chipset mode */
144		*val = (u_long)ppb_get_mode(bus);
145		ppbdev->mode = (u_short)*val;
146		break;
147	case PPBUS_IVAR_AVM:
148		*val = (u_long)ppbdev->avm;
149		break;
150	case PPBUS_IVAR_IRQ:
151		BUS_READ_IVAR(device_get_parent(bus), bus, PPC_IVAR_IRQ, val);
152		break;
153	default:
154		return (ENOENT);
155	}
156
157	return (0);
158}
159
160static int
161ppbus_write_ivar(device_t bus, device_t dev, int index, u_long val)
162{
163	struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(dev);
164
165	switch (index) {
166	case PPBUS_IVAR_MODE:
167		/* XXX yet device mode = ppbus mode = chipset mode */
168		ppb_set_mode(bus,val);
169		ppbdev->mode = ppb_get_mode(bus);
170		break;
171	default:
172		return (ENOENT);
173  	}
174
175	return (0);
176  }
177
178#define PPB_PNP_PRINTER		0
179#define PPB_PNP_MODEM		1
180#define PPB_PNP_NET		2
181#define PPB_PNP_HDC		3
182#define PPB_PNP_PCMCIA		4
183#define PPB_PNP_MEDIA		5
184#define PPB_PNP_FDC		6
185#define PPB_PNP_PORTS		7
186#define PPB_PNP_SCANNER		8
187#define PPB_PNP_DIGICAM		9
188
189#ifndef DONTPROBE_1284
190
191static char *pnp_tokens[] = {
192	"PRINTER", "MODEM", "NET", "HDC", "PCMCIA", "MEDIA",
193	"FDC", "PORTS", "SCANNER", "DIGICAM", "", NULL };
194
195#if 0
196static char *pnp_classes[] = {
197	"printer", "modem", "network device",
198	"hard disk", "PCMCIA", "multimedia device",
199	"floppy disk", "ports", "scanner",
200	"digital camera", "unknown device", NULL };
201#endif
202
203/*
204 * search_token()
205 *
206 * Search the first occurence of a token within a string
207 *
208 * XXX should use strxxx() calls
209 */
210static char *
211search_token(char *str, int slen, char *token)
212{
213	char *p;
214	int tlen, i, j;
215
216#define UNKNOWN_LENGTH	-1
217
218	if (slen == UNKNOWN_LENGTH)
219		/* get string's length */
220		for (slen = 0, p = str; *p != '\0'; p++)
221			slen ++;
222
223	/* get token's length */
224	for (tlen = 0, p = token; *p != '\0'; p++)
225		tlen ++;
226
227	if (tlen == 0)
228		return (str);
229
230	for (i = 0; i <= slen-tlen; i++) {
231		for (j = 0; j < tlen; j++)
232			if (str[i+j] != token[j])
233				break;
234		if (j == tlen)
235			return (&str[i]);
236	}
237
238	return (NULL);
239}
240
241/*
242 * ppb_pnp_detect()
243 *
244 * Returns the class id. of the peripherial, -1 otherwise
245 */
246static int
247ppb_pnp_detect(device_t bus)
248{
249	char *token, *class = 0;
250	int i, len, error;
251	int class_id = -1;
252	char str[PPB_PnP_STRING_SIZE+1];
253	int unit = device_get_unit(bus);
254
255	printf("Probing for PnP devices on ppbus%d:\n", unit);
256
257	if ((error = ppb_1284_read_id(bus, PPB_NIBBLE, str,
258					PPB_PnP_STRING_SIZE, &len)))
259		goto end_detect;
260
261#ifdef DEBUG_1284
262	printf("ppb: <PnP> %d characters: ", len);
263	for (i = 0; i < len; i++)
264		printf("%c(0x%x) ", str[i], str[i]);
265	printf("\n");
266#endif
267
268	/* replace ';' characters by '\0' */
269	for (i = 0; i < len; i++)
270		str[i] = (str[i] == ';') ? '\0' : str[i];
271
272	if ((token = search_token(str, len, "MFG")) != NULL ||
273		(token = search_token(str, len, "MANUFACTURER")) != NULL)
274		printf("ppbus%d: <%s", unit,
275			search_token(token, UNKNOWN_LENGTH, ":") + 1);
276	else
277		printf("ppbus%d: <unknown", unit);
278
279	if ((token = search_token(str, len, "MDL")) != NULL ||
280		(token = search_token(str, len, "MODEL")) != NULL)
281		printf(" %s",
282			search_token(token, UNKNOWN_LENGTH, ":") + 1);
283	else
284		printf(" unknown");
285
286	if ((token = search_token(str, len, "VER")) != NULL)
287		printf("/%s",
288			search_token(token, UNKNOWN_LENGTH, ":") + 1);
289
290	if ((token = search_token(str, len, "REV")) != NULL)
291		printf(".%s",
292			search_token(token, UNKNOWN_LENGTH, ":") + 1);
293
294	printf(">");
295
296	if ((token = search_token(str, len, "CLS")) != NULL) {
297		class = search_token(token, UNKNOWN_LENGTH, ":") + 1;
298		printf(" %s", class);
299	}
300
301	if ((token = search_token(str, len, "CMD")) != NULL ||
302		(token = search_token(str, len, "COMMAND")) != NULL)
303		printf(" %s",
304			search_token(token, UNKNOWN_LENGTH, ":") + 1);
305
306	printf("\n");
307
308	if (class)
309		/* identify class ident */
310		for (i = 0; pnp_tokens[i] != NULL; i++) {
311			if (search_token(class, len, pnp_tokens[i]) != NULL) {
312				class_id = i;
313				goto end_detect;
314			}
315		}
316
317	class_id = PPB_PnP_UNKNOWN;
318
319end_detect:
320	return (class_id);
321}
322
323/*
324 * ppb_scan_bus()
325 *
326 * Scan the ppbus for IEEE1284 compliant devices
327 */
328static int
329ppb_scan_bus(device_t bus)
330{
331	struct ppb_data * ppb = (struct ppb_data *)device_get_softc(bus);
332	int error = 0;
333	int unit = device_get_unit(bus);
334
335	/* try all IEEE1284 modes, for one device only
336	 *
337	 * XXX We should implement the IEEE1284.3 standard to detect
338	 * daisy chained devices
339	 */
340
341	error = ppb_1284_negociate(bus, PPB_NIBBLE, PPB_REQUEST_ID);
342
343	if ((ppb->state == PPB_ERROR) && (ppb->error == PPB_NOT_IEEE1284))
344		goto end_scan;
345
346	ppb_1284_terminate(bus);
347
348	printf("ppbus%d: IEEE1284 device found ", unit);
349
350	if (!(error = ppb_1284_negociate(bus, PPB_NIBBLE, 0))) {
351		printf("/NIBBLE");
352		ppb_1284_terminate(bus);
353	}
354
355	if (!(error = ppb_1284_negociate(bus, PPB_PS2, 0))) {
356		printf("/PS2");
357		ppb_1284_terminate(bus);
358	}
359
360	if (!(error = ppb_1284_negociate(bus, PPB_ECP, 0))) {
361		printf("/ECP");
362		ppb_1284_terminate(bus);
363	}
364
365	if (!(error = ppb_1284_negociate(bus, PPB_ECP, PPB_USE_RLE))) {
366		printf("/ECP_RLE");
367		ppb_1284_terminate(bus);
368	}
369
370	if (!(error = ppb_1284_negociate(bus, PPB_EPP, 0))) {
371		printf("/EPP");
372		ppb_1284_terminate(bus);
373	}
374
375	/* try more IEEE1284 modes */
376	if (bootverbose) {
377		if (!(error = ppb_1284_negociate(bus, PPB_NIBBLE,
378				PPB_REQUEST_ID))) {
379			printf("/NIBBLE_ID");
380			ppb_1284_terminate(bus);
381		}
382
383		if (!(error = ppb_1284_negociate(bus, PPB_PS2,
384				PPB_REQUEST_ID))) {
385			printf("/PS2_ID");
386			ppb_1284_terminate(bus);
387		}
388
389		if (!(error = ppb_1284_negociate(bus, PPB_ECP,
390				PPB_REQUEST_ID))) {
391			printf("/ECP_ID");
392			ppb_1284_terminate(bus);
393		}
394
395		if (!(error = ppb_1284_negociate(bus, PPB_ECP,
396				PPB_REQUEST_ID | PPB_USE_RLE))) {
397			printf("/ECP_RLE_ID");
398			ppb_1284_terminate(bus);
399		}
400
401		if (!(error = ppb_1284_negociate(bus, PPB_COMPATIBLE,
402				PPB_EXTENSIBILITY_LINK))) {
403			printf("/Extensibility Link");
404			ppb_1284_terminate(bus);
405		}
406	}
407
408	printf("\n");
409
410	/* detect PnP devices */
411	ppb->class_id = ppb_pnp_detect(bus);
412
413	return (0);
414
415end_scan:
416	return (error);
417}
418
419#endif /* !DONTPROBE_1284 */
420
421static int
422ppbus_attach(device_t dev)
423{
424	int i;
425	int unit, disabled;
426	char *name;
427
428	/*
429	 * Add all devices configured to be attached to ppbus0.
430	 */
431	for (i = resource_query_string(-1, "at", "ppbus0");
432	     i != -1;
433	     i = resource_query_string(i, "at", "ppbus0")) {
434		unit = resource_query_unit(i);
435		name = resource_query_name(i);
436		if (resource_int_value(name, unit, "disabled", &disabled) == 0) {
437			if (disabled)
438				continue;
439		}
440		ppbus_add_device(dev, name, unit);
441	}
442
443	/*
444	 * and ppbus?
445	 */
446	for (i = resource_query_string(-1, "at", "ppbus");
447	     i != -1;
448	     i = resource_query_string(i, "at", "ppbus")) {
449		unit = resource_query_unit(i);
450		name = resource_query_name(i);
451		if (resource_int_value(name, unit, "disabled", &disabled) == 0) {
452			if (disabled)
453				continue;
454		}
455		ppbus_add_device(dev, name, unit);
456	}
457
458#ifndef DONTPROBE_1284
459	/* detect IEEE1284 compliant devices */
460	ppb_scan_bus(dev);
461#endif /* !DONTPROBE_1284 */
462
463	/* launch attachement of the added children */
464	bus_generic_attach(dev);
465
466	return 0;
467}
468
469static int
470ppbus_setup_intr(device_t bus, device_t child, struct resource *r, int flags,
471			void (*ihand)(void *), void *arg, void **cookiep)
472{
473	int error;
474	struct ppb_data *ppb = DEVTOSOFTC(bus);
475	struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(child);
476
477	/* a device driver must own the bus to register an interrupt */
478	if (ppb->ppb_owner != child)
479		return (EINVAL);
480
481	if ((error = BUS_SETUP_INTR(device_get_parent(bus), child, r, flags,
482					ihand, arg, cookiep)))
483		return (error);
484
485	/* store the resource and the cookie for eventually forcing
486	 * handler unregistration
487	 */
488	ppbdev->intr_cookie = *cookiep;
489	ppbdev->intr_resource = r;
490
491	return (0);
492}
493
494static int
495ppbus_teardown_intr(device_t bus, device_t child, struct resource *r, void *ih)
496{
497	struct ppb_data *ppb = DEVTOSOFTC(bus);
498	struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(child);
499
500	/* a device driver must own the bus to unregister an interrupt */
501	if ((ppb->ppb_owner != child) || (ppbdev->intr_cookie != ih) ||
502			(ppbdev->intr_resource != r))
503		return (EINVAL);
504
505	ppbdev->intr_cookie = 0;
506	ppbdev->intr_resource = 0;
507
508	/* pass unregistration to the upper layer */
509	return (BUS_TEARDOWN_INTR(device_get_parent(bus), child, r, ih));
510}
511
512/*
513 * ppb_request_bus()
514 *
515 * Allocate the device to perform transfers.
516 *
517 * how	: PPB_WAIT or PPB_DONTWAIT
518 */
519int
520ppb_request_bus(device_t bus, device_t dev, int how)
521{
522	int s, error = 0;
523	struct ppb_data *ppb = DEVTOSOFTC(bus);
524	struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(dev);
525
526	while (!error) {
527		s = splhigh();
528		if (ppb->ppb_owner) {
529			splx(s);
530
531			switch (how) {
532			case (PPB_WAIT | PPB_INTR):
533				error = tsleep(ppb, PPBPRI|PCATCH, "ppbreq", 0);
534				break;
535
536			case (PPB_WAIT | PPB_NOINTR):
537				error = tsleep(ppb, PPBPRI, "ppbreq", 0);
538				break;
539
540			default:
541				return (EWOULDBLOCK);
542				break;
543			}
544
545		} else {
546			ppb->ppb_owner = dev;
547
548			/* restore the context of the device
549			 * The first time, ctx.valid is certainly false
550			 * then do not change anything. This is usefull for
551			 * drivers that do not set there operating mode
552			 * during attachement
553			 */
554			if (ppbdev->ctx.valid)
555				ppb_set_mode(bus, ppbdev->ctx.mode);
556
557			splx(s);
558			return (0);
559		}
560	}
561
562	return (error);
563}
564
565/*
566 * ppb_release_bus()
567 *
568 * Release the device allocated with ppb_request_dev()
569 */
570int
571ppb_release_bus(device_t bus, device_t dev)
572{
573	int s, error;
574	struct ppb_data *ppb = DEVTOSOFTC(bus);
575	struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(dev);
576
577	if (ppbdev->intr_resource != 0)
578		/* force interrupt handler unregistration when the ppbus is released */
579		if ((error = BUS_TEARDOWN_INTR(bus, dev, ppbdev->intr_resource,
580					       ppbdev->intr_cookie)))
581			return (error);
582
583	s = splhigh();
584	if (ppb->ppb_owner != dev) {
585		splx(s);
586		return (EACCES);
587	}
588
589	ppb->ppb_owner = 0;
590	splx(s);
591
592	/* save the context of the device */
593	ppbdev->ctx.mode = ppb_get_mode(bus);
594
595	/* ok, now the context of the device is valid */
596	ppbdev->ctx.valid = 1;
597
598	/* wakeup waiting processes */
599	wakeup(ppb);
600
601	return (0);
602}
603
604DRIVER_MODULE(ppbus, ppc, ppbus_driver, ppbus_devclass, 0, 0);
605