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 26spinlock __haiku_intr_spinlock; 27 28struct net_stack_module_info *gStack; 29 30static struct list sRootDevices; 31static int sNextUnit; 32 33// #pragma mark - private functions 34 35 36static device_t 37init_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 49static device_t 50new_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 67static image_id 68find_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 85static device_method_signature_t 86resolve_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 106void 107driver_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 116static int 117driver_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 131int 132driver_vprintf(const char *format, va_list vl) 133{ 134 return driver_vprintf_etc(NULL, format, vl); 135} 136 137 138int 139device_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 150void 151device_set_desc(device_t dev, const char *desc) 152{ 153 dev->description = desc; 154} 155 156 157void 158device_set_desc_copy(device_t dev, const char *desc) 159{ 160 dev->description = strdup(desc); 161 dev->flags |= DEVICE_DESC_ALLOCED; 162} 163 164 165const char * 166device_get_desc(device_t dev) 167{ 168 return dev->description; 169} 170 171 172device_t 173device_get_parent(device_t dev) 174{ 175 return dev->parent; 176} 177 178 179devclass_t 180device_get_devclass(device_t dev) 181{ 182 // TODO find out what to do 183 return 0; 184} 185 186 187int 188device_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 222void 223device_set_ivars(device_t dev, void *ivars) 224{ 225 dev->ivars = ivars; 226} 227 228 229void * 230device_get_ivars(device_t dev) 231{ 232 return dev->ivars; 233} 234 235 236const char * 237device_get_name(device_t dev) 238{ 239 if (dev == NULL) 240 return NULL; 241 242 return dev->device_name; 243} 244 245 246int 247device_get_unit(device_t dev) 248{ 249 return dev->unit; 250} 251 252 253const char * 254device_get_nameunit(device_t dev) 255{ 256 return dev->nameunit; 257} 258 259 260void * 261device_get_softc(device_t dev) 262{ 263 return dev->softc; 264} 265 266 267void 268device_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 286u_int32_t 287device_get_flags(device_t dev) 288{ 289 return dev->flags; 290} 291 292 293int 294device_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 353int 354device_is_alive(device_t device) 355{ 356 return (device->flags & DEVICE_ATTACHED) != 0; 357} 358 359 360device_t 361device_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 408device_t 409device_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*/ 417int 418device_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 468int 469device_is_attached(device_t device) 470{ 471 return (device->flags & DEVICE_ATTACHED) != 0; 472} 473 474 475int 476device_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 496int 497device_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 523int 524bus_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 552int 553bus_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 575device_t 576find_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 589driver_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 617int 618printf(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 631int 632resource_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 640int 641resource_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