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