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