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