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