Deleted Added
sdiff udiff text old ( 50477 ) new ( 55939 )
full compact
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: 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);