1 /* 2 * Copyright 2007, Hugo Santos, hugosantos@gmail.com. All Rights Reserved. 3 * Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All Rights Reserved. 4 * Copyright 2004, Marcus Overhagen. All Rights Reserved. 5 * 6 * Distributed under the terms of the MIT License. 7 */ 8 9 10 #include "device.h" 11 12 #include <stdio.h> 13 14 #include <KernelExport.h> 15 #include <image.h> 16 #include <kernel/heap.h> 17 18 #include <compat/machine/resource.h> 19 #include <compat/dev/mii/mii.h> 20 #include <compat/sys/bus.h> 21 #include <compat/net/if_media.h> 22 23 #include <compat/dev/mii/miivar.h> 24 25 26 spinlock __haiku_intr_spinlock; 27 28 struct net_stack_module_info *gStack; 29 pci_module_info *gPci; 30 struct pci_x86_module_info *gPCIx86; 31 32 static struct list sRootDevices; 33 static int sNextUnit; 34 35 // #pragma mark - private functions 36 37 38 static device_t 39 init_device(device_t device, driver_t *driver) 40 { 41 list_init_etc(&device->children, offsetof(struct device, link)); 42 device->unit = sNextUnit++; 43 44 if (driver != NULL && device_set_driver(device, driver) < 0) 45 return NULL; 46 47 return device; 48 } 49 50 51 static device_t 52 new_device(driver_t *driver) 53 { 54 device_t dev = malloc(sizeof(struct device)); 55 if (dev == NULL) 56 return NULL; 57 58 memset(dev, 0, sizeof(struct device)); 59 60 if (init_device(dev, driver) == NULL) { 61 free(dev); 62 return NULL; 63 } 64 65 return dev; 66 } 67 68 69 static image_id 70 find_own_image() 71 { 72 int32 cookie = 0; 73 image_info info; 74 while (get_next_image_info(B_SYSTEM_TEAM, &cookie, &info) == B_OK) { 75 if (((addr_t)info.text <= (addr_t)find_own_image 76 && (addr_t)info.text + (addr_t)info.text_size 77 > (addr_t)find_own_image)) { 78 // found our own image 79 return info.id; 80 } 81 } 82 83 return B_ENTRY_NOT_FOUND; 84 } 85 86 87 static device_method_signature_t 88 resolve_method(driver_t *driver, const char *name) 89 { 90 device_method_signature_t method = NULL; 91 int i; 92 93 for (i = 0; method == NULL && driver->methods[i].name != NULL; i++) { 94 if (strcmp(driver->methods[i].name, name) == 0) 95 method = driver->methods[i].method; 96 } 97 98 if (method == NULL) 99 panic("resolve_method: method%s not found\n", name); 100 101 return method; 102 } 103 104 105 // #pragma mark - Device 106 107 108 void 109 driver_printf(const char *format, ...) 110 { 111 va_list vl; 112 va_start(vl, format); 113 driver_vprintf(format, vl); 114 va_end(vl); 115 } 116 117 118 static int 119 driver_vprintf_etc(const char *extra, const char *format, va_list vl) 120 { 121 char buf[256]; 122 int ret = vsnprintf(buf, sizeof(buf), format, vl); 123 124 if (extra) 125 dprintf("[%s] (%s) %s", gDriverName, extra, buf); 126 else 127 dprintf("[%s] %s", gDriverName, buf); 128 129 return ret; 130 } 131 132 133 int 134 driver_vprintf(const char *format, va_list vl) 135 { 136 return driver_vprintf_etc(NULL, format, vl); 137 } 138 139 140 int 141 device_printf(device_t dev, const char *format, ...) 142 { 143 va_list vl; 144 145 va_start(vl, format); 146 driver_vprintf_etc(dev->device_name, format, vl); 147 va_end(vl); 148 return 0; 149 } 150 151 152 void 153 device_set_desc(device_t dev, const char *desc) 154 { 155 dev->description = desc; 156 } 157 158 159 void 160 device_set_desc_copy(device_t dev, const char *desc) 161 { 162 dev->description = strdup(desc); 163 dev->flags |= DEVICE_DESC_ALLOCED; 164 } 165 166 167 const char * 168 device_get_desc(device_t dev) 169 { 170 return dev->description; 171 } 172 173 174 device_t 175 device_get_parent(device_t dev) 176 { 177 return dev->parent; 178 } 179 180 181 devclass_t 182 device_get_devclass(device_t dev) 183 { 184 // TODO find out what to do 185 return 0; 186 } 187 188 189 int 190 device_get_children(device_t dev, device_t **devlistp, int *devcountp) 191 { 192 int count; 193 device_t child = NULL; 194 device_t *list; 195 196 count = 0; 197 while ((child = list_get_next_item(&dev->children, child)) != NULL) { 198 count++; 199 } 200 201 if (count == 0) { 202 *devlistp = NULL; 203 *devcountp = 0; 204 return (0); 205 } 206 207 list = malloc(count * sizeof(device_t)); 208 if (!list) 209 return (ENOMEM); 210 211 count = 0; 212 while ((child = list_get_next_item(&dev->children, child)) != NULL) { 213 list[count] = child; 214 count++; 215 } 216 217 *devlistp = list; 218 *devcountp = count; 219 220 return (0); 221 } 222 223 224 void 225 device_set_ivars(device_t dev, void *ivars) 226 { 227 dev->ivars = ivars; 228 } 229 230 231 void * 232 device_get_ivars(device_t dev) 233 { 234 return dev->ivars; 235 } 236 237 238 const char * 239 device_get_name(device_t dev) 240 { 241 if (dev == NULL) 242 return NULL; 243 244 return dev->device_name; 245 } 246 247 248 int 249 device_get_unit(device_t dev) 250 { 251 return dev->unit; 252 } 253 254 255 const char * 256 device_get_nameunit(device_t dev) 257 { 258 return dev->nameunit; 259 } 260 261 262 void * 263 device_get_softc(device_t dev) 264 { 265 return dev->softc; 266 } 267 268 269 void 270 device_set_softc(device_t dev, void *softc) 271 { 272 if (dev->softc == softc) 273 return; 274 275 if ((dev->flags & DEVICE_SOFTC_SET) == 0) { 276 // Not externally allocated. We own it so we must clean it up. 277 free(dev->softc); 278 } 279 280 dev->softc = softc; 281 if (dev->softc != NULL) 282 dev->flags |= DEVICE_SOFTC_SET; 283 else 284 dev->flags &= ~DEVICE_SOFTC_SET; 285 } 286 287 288 u_int32_t 289 device_get_flags(device_t dev) 290 { 291 return dev->flags; 292 } 293 294 295 int 296 device_set_driver(device_t dev, driver_t *driver) 297 { 298 int i; 299 300 dev->softc = malloc(driver->size); 301 if (dev->softc == NULL) 302 return -1; 303 304 memset(dev->softc, 0, driver->size); 305 dev->driver = driver; 306 307 for (i = 0; driver->methods[i].name != NULL; i++) { 308 device_method_t *mth = &driver->methods[i]; 309 310 if (strcmp(mth->name, "device_register") == 0) 311 dev->methods.device_register = (void *)mth->method; 312 else if (strcmp(mth->name, "device_probe") == 0) 313 dev->methods.probe = (void *)mth->method; 314 else if (strcmp(mth->name, "device_attach") == 0) 315 dev->methods.attach = (void *)mth->method; 316 else if (strcmp(mth->name, "device_detach") == 0) 317 dev->methods.detach = (void *)mth->method; 318 else if (strcmp(mth->name, "device_suspend") == 0) 319 dev->methods.suspend = (void *)mth->method; 320 else if (strcmp(mth->name, "device_resume") == 0) 321 dev->methods.resume = (void *)mth->method; 322 else if (strcmp(mth->name, "device_shutdown") == 0) 323 dev->methods.shutdown = (void *)mth->method; 324 else if (strcmp(mth->name, "miibus_readreg") == 0) 325 dev->methods.miibus_readreg = (void *)mth->method; 326 else if (strcmp(mth->name, "miibus_writereg") == 0) 327 dev->methods.miibus_writereg = (void *)mth->method; 328 else if (strcmp(mth->name, "miibus_statchg") == 0) 329 dev->methods.miibus_statchg = (void *)mth->method; 330 else if (!strcmp(mth->name, "miibus_linkchg")) 331 dev->methods.miibus_linkchg = (void *)mth->method; 332 else if (!strcmp(mth->name, "miibus_mediainit")) 333 dev->methods.miibus_mediainit = (void *)mth->method; 334 else if (!strcmp(mth->name, "bus_child_location_str")) 335 dev->methods.bus_child_location_str = (void *)mth->method; 336 else if (!strcmp(mth->name, "bus_child_pnpinfo_str")) 337 dev->methods.bus_child_pnpinfo_str = (void *)mth->method; 338 else if (!strcmp(mth->name, "bus_hinted_child")) 339 dev->methods.bus_hinted_child = (void *)mth->method; 340 else if (!strcmp(mth->name, "bus_print_child")) 341 dev->methods.bus_print_child = (void *)mth->method; 342 else if (!strcmp(mth->name, "bus_read_ivar")) 343 dev->methods.bus_read_ivar = (void *)mth->method; 344 else if (!strcmp(mth->name, "bus_get_dma_tag")) 345 dev->methods.bus_get_dma_tag = (void *)mth->method; 346 else 347 panic("device_set_driver: method %s not found\n", mth->name); 348 349 } 350 351 return 0; 352 } 353 354 355 int 356 device_is_alive(device_t device) 357 { 358 return (device->flags & DEVICE_ATTACHED) != 0; 359 } 360 361 362 device_t 363 device_add_child_driver(device_t parent, const char* name, driver_t* _driver, 364 int unit) 365 { 366 device_t child = NULL; 367 368 if (_driver == NULL && name != NULL) { 369 if (strcmp(name, "miibus") == 0) 370 child = new_device(&miibus_driver); 371 else { 372 // find matching driver structure 373 driver_t** driver; 374 char symbol[128]; 375 376 snprintf(symbol, sizeof(symbol), "__fbsd_%s_%s", name, 377 parent->driver->name); 378 if (get_image_symbol(find_own_image(), symbol, B_SYMBOL_TYPE_DATA, 379 (void**)&driver) == B_OK) { 380 child = new_device(*driver); 381 } else 382 device_printf(parent, "couldn't find symbol %s\n", symbol); 383 } 384 } else if (_driver != NULL) { 385 child = new_device(_driver); 386 } else 387 child = new_device(NULL); 388 389 if (child == NULL) 390 return NULL; 391 392 if (name != NULL) 393 strlcpy(child->device_name, name, sizeof(child->device_name)); 394 395 child->parent = parent; 396 397 if (parent != NULL) { 398 list_add_item(&parent->children, child); 399 child->root = parent->root; 400 } else { 401 if (sRootDevices.link.next == NULL) 402 list_init_etc(&sRootDevices, offsetof(struct device, link)); 403 list_add_item(&sRootDevices, child); 404 } 405 406 return child; 407 } 408 409 410 device_t 411 device_add_child(device_t parent, const char* name, int unit) 412 { 413 return device_add_child_driver(parent, name, NULL, unit); 414 } 415 416 417 /*! Delete the child and all of its children. Detach as necessary. 418 */ 419 int 420 device_delete_child(device_t parent, device_t child) 421 { 422 int status; 423 424 if (child == NULL) 425 return 0; 426 427 if (parent != NULL) 428 list_remove_item(&parent->children, child); 429 else 430 list_remove_item(&sRootDevices, child); 431 432 // We differentiate from the FreeBSD logic here - it will first delete 433 // the children, and will then detach the device. 434 // This has the problem that you cannot safely call device_delete_child() 435 // as you don't know if one of the children deletes its own children this 436 // way when it is detached. 437 // Therefore, we'll detach first, and then delete whatever is left. 438 439 parent = child; 440 child = NULL; 441 442 // detach children 443 while ((child = list_get_next_item(&parent->children, child)) != NULL) { 444 device_detach(child); 445 } 446 447 // detach device 448 status = device_detach(parent); 449 if (status != 0) 450 return status; 451 452 // delete children 453 while ((child = list_get_first_item(&parent->children)) != NULL) { 454 device_delete_child(parent, child); 455 } 456 457 // delete device 458 if (parent->flags & DEVICE_DESC_ALLOCED) 459 free((char *)parent->description); 460 461 // Delete softc if we were the ones to allocate it. 462 if ((parent->flags & DEVICE_SOFTC_SET) == 0) 463 free(parent->softc); 464 465 free(parent); 466 return 0; 467 } 468 469 470 int 471 device_is_attached(device_t device) 472 { 473 return (device->flags & DEVICE_ATTACHED) != 0; 474 } 475 476 477 int 478 device_attach(device_t device) 479 { 480 int result; 481 482 if (device->driver == NULL 483 || device->methods.attach == NULL) 484 return B_ERROR; 485 486 result = device->methods.attach(device); 487 488 if (result == 0) 489 atomic_or(&device->flags, DEVICE_ATTACHED); 490 491 if (result == 0 && HAIKU_DRIVER_REQUIRES(FBSD_WLAN_FEATURE)) 492 result = start_wlan(device); 493 494 return result; 495 } 496 497 498 int 499 device_detach(device_t device) 500 { 501 if (device->driver == NULL) 502 return B_ERROR; 503 504 if ((atomic_and(&device->flags, ~DEVICE_ATTACHED) & DEVICE_ATTACHED) != 0 505 && device->methods.detach != NULL) { 506 int result = 0; 507 if (HAIKU_DRIVER_REQUIRES(FBSD_WLAN_FEATURE)) 508 result = stop_wlan(device); 509 if (result != 0 && result != B_BAD_VALUE) { 510 atomic_or(&device->flags, DEVICE_ATTACHED); 511 return result; 512 } 513 514 result = device->methods.detach(device); 515 if (result != 0) { 516 atomic_or(&device->flags, DEVICE_ATTACHED); 517 return result; 518 } 519 } 520 521 return 0; 522 } 523 524 525 int 526 bus_generic_attach(device_t dev) 527 { 528 device_t child = NULL; 529 530 while ((child = list_get_next_item(&dev->children, child)) != NULL) { 531 if (child->driver == NULL) { 532 driver_t *driver = __haiku_select_miibus_driver(child); 533 if (driver == NULL) { 534 struct mii_attach_args *ma = device_get_ivars(child); 535 536 device_printf(dev, "No PHY module found (%x/%x)!\n", 537 MII_OUI(ma->mii_id1, ma->mii_id2), MII_MODEL(ma->mii_id2)); 538 } else 539 device_set_driver(child, driver); 540 } else 541 child->methods.probe(child); 542 543 if (child->driver != NULL) { 544 int result = device_attach(child); 545 if (result != 0) 546 return result; 547 } 548 } 549 550 return 0; 551 } 552 553 554 int 555 bus_generic_detach(device_t device) 556 { 557 device_t child = NULL; 558 559 if ((device->flags & DEVICE_ATTACHED) == 0) 560 return B_ERROR; 561 562 while (true) { 563 child = list_get_next_item(&device->children, child); 564 if (child == NULL) 565 break; 566 567 device_detach(child); 568 } 569 570 return 0; 571 } 572 573 574 // #pragma mark - Misc, Malloc 575 576 577 device_t 578 find_root_device(int unit) 579 { 580 device_t device = NULL; 581 582 while ((device = list_get_next_item(&sRootDevices, device)) != NULL) { 583 if (device->unit <= unit) 584 return device; 585 } 586 587 return NULL; 588 } 589 590 591 driver_t * 592 __haiku_probe_miibus(device_t dev, driver_t *drivers[]) 593 { 594 driver_t *selected = NULL; 595 int i, selectedResult = 0; 596 597 if (drivers == NULL) 598 return NULL; 599 600 for (i = 0; drivers[i]; i++) { 601 device_probe_t *probe = (device_probe_t *) 602 resolve_method(drivers[i], "device_probe"); 603 if (probe) { 604 int result = probe(dev); 605 if (result >= 0) { 606 if (selected == NULL || result < selectedResult) { 607 selected = drivers[i]; 608 selectedResult = result; 609 device_printf(dev, "Found MII: %s\n", selected->name); 610 } 611 } 612 } 613 } 614 615 return selected; 616 } 617 618 619 int 620 printf(const char *format, ...) 621 { 622 char buf[256]; 623 va_list vl; 624 va_start(vl, format); 625 vsnprintf(buf, sizeof(buf), format, vl); 626 va_end(vl); 627 dprintf("%s", buf); 628 629 return 0; 630 } 631 632 633 int 634 resource_int_value(const char *name, int unit, const char *resname, 635 int *result) 636 { 637 /* no support for hints */ 638 return -1; 639 } 640 641 642 int 643 resource_disabled(const char *name, int unit) 644 { 645 int error, value; 646 647 error = resource_int_value(name, unit, "disabled", &value); 648 if (error) 649 return (0); 650 return (value); 651 } 652