1 /* 2 * Copyright 2007-2010 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT license. 4 * 5 * Authors: 6 * Gerald Zajac 7 */ 8 9 #include <KernelExport.h> 10 #include <PCI.h> 11 #include <malloc.h> 12 #include <stdio.h> 13 #include <string.h> 14 #include <graphic_driver.h> 15 #ifdef __HAIKU__ 16 #include <boot_item.h> 17 #endif // __HAIKU__ 18 19 #include "DriverInterface.h" 20 21 22 #undef TRACE 23 24 #ifdef ENABLE_DEBUG_TRACE 25 # define TRACE(x...) dprintf("3dfx: " x) 26 #else 27 # define TRACE(x...) ; 28 #endif 29 30 31 #define ACCELERANT_NAME "3dfx.accelerant" 32 33 #define ROUND_TO_PAGE_SIZE(x) (((x) + (B_PAGE_SIZE) - 1) & ~((B_PAGE_SIZE) - 1)) 34 35 #define SKD_HANDLER_INSTALLED 0x80000000 36 #define MAX_DEVICES 4 37 #define DEVICE_FORMAT "%04X_%04X_%02X%02X%02X" 38 39 int32 api_version = B_CUR_DRIVER_API_VERSION; // revision of driver API used 40 41 #define VENDOR_ID 0x121A // 3DFX vendor ID 42 43 44 struct ChipInfo { 45 uint16 chipID; // PCI device id of the chip 46 ChipType chipType; // assigned chip type identifier 47 const char* chipName; // user recognizable name for chip 48 // (must be < 32 chars) 49 }; 50 51 52 // This table maps a PCI device ID to a chip type identifier and the chip name. 53 54 static const ChipInfo chipTable[] = { 55 { 0x03, BANSHEE, "Banshee" }, 56 { 0x05, VOODOO_3, "Voodoo 3" }, 57 { 0x09, VOODOO_5, "Voodoo 5" }, 58 { 0, TDFX_NONE, NULL } 59 }; 60 61 62 struct DeviceInfo { 63 uint32 openCount; // count of how many times device has been opened 64 int32 flags; 65 area_id sharedArea; // area shared between driver and all accelerants 66 SharedInfo* sharedInfo; // pointer to shared info area memory 67 vuint8* regs; // pointer to memory mapped registers 68 const ChipInfo* pChipInfo; // info about the selected chip 69 pci_info pciInfo; // copy of pci info for this device 70 char name[B_OS_NAME_LENGTH]; // name of device 71 }; 72 73 74 static Benaphore gLock; 75 static DeviceInfo gDeviceInfo[MAX_DEVICES]; 76 static char* gDeviceNames[MAX_DEVICES + 1]; 77 static pci_module_info* gPCI; 78 79 80 // Prototypes for device hook functions. 81 82 static status_t device_open(const char* name, uint32 flags, void** cookie); 83 static status_t device_close(void* dev); 84 static status_t device_free(void* dev); 85 static status_t device_read(void* dev, off_t pos, void* buf, size_t* len); 86 static status_t device_write(void* dev, off_t pos, const void* buf, 87 size_t* len); 88 static status_t device_ioctl(void* dev, uint32 msg, void* buf, size_t len); 89 90 static device_hooks gDeviceHooks = 91 { 92 device_open, 93 device_close, 94 device_free, 95 device_ioctl, 96 device_read, 97 device_write, 98 NULL, 99 NULL, 100 NULL, 101 NULL 102 }; 103 104 105 106 static inline uint32 107 GetPCI(pci_info& info, uint8 offset, uint8 size) 108 { 109 return gPCI->read_pci_config(info.bus, info.device, info.function, offset, 110 size); 111 } 112 113 114 static inline void 115 SetPCI(pci_info& info, uint8 offset, uint8 size, uint32 value) 116 { 117 gPCI->write_pci_config(info.bus, info.device, info.function, offset, size, 118 value); 119 } 120 121 122 static status_t 123 MapDevice(DeviceInfo& di) 124 { 125 SharedInfo& si = *(di.sharedInfo); 126 pci_info& pciInfo = di.pciInfo; 127 128 TRACE("enter MapDevice()\n"); 129 130 // Enable memory mapped IO and bus master. 131 132 SetPCI(pciInfo, PCI_command, 2, GetPCI(pciInfo, PCI_command, 2) 133 | PCI_command_io | PCI_command_memory | PCI_command_master); 134 135 // Map the video memory. 136 137 phys_addr_t videoRamAddr = pciInfo.u.h0.base_registers[1]; 138 uint32 videoRamSize = pciInfo.u.h0.base_register_sizes[1]; 139 si.videoMemPCI = videoRamAddr; 140 char frameBufferAreaName[] = "3DFX frame buffer"; 141 142 si.videoMemArea = map_physical_memory( 143 frameBufferAreaName, 144 videoRamAddr, 145 videoRamSize, 146 B_ANY_KERNEL_BLOCK_ADDRESS | B_WRITE_COMBINING_MEMORY, 147 B_READ_AREA + B_WRITE_AREA, 148 (void**)&si.videoMemAddr); 149 150 TRACE("Video memory, area: %ld, addr: 0x%lX, size: %ld\n", 151 si.videoMemArea, (uint32)(si.videoMemAddr), videoRamSize); 152 153 if (si.videoMemArea < 0) { 154 // Try to map this time without write combining. 155 si.videoMemArea = map_physical_memory( 156 frameBufferAreaName, 157 videoRamAddr, 158 videoRamSize, 159 B_ANY_KERNEL_BLOCK_ADDRESS, 160 B_READ_AREA + B_WRITE_AREA, 161 (void**)&si.videoMemAddr); 162 } 163 164 if (si.videoMemArea < 0) 165 return si.videoMemArea; 166 167 // Map the MMIO register area. 168 169 phys_addr_t regsBase = pciInfo.u.h0.base_registers[0]; 170 uint32 regAreaSize = pciInfo.u.h0.base_register_sizes[0]; 171 172 si.regsArea = map_physical_memory("3DFX mmio registers", 173 regsBase, 174 regAreaSize, 175 B_ANY_KERNEL_ADDRESS, 176 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_CLONEABLE_AREA, 177 (void**)&di.regs); 178 179 // If there was an error, delete other areas. 180 if (si.regsArea < 0) { 181 delete_area(si.videoMemArea); 182 si.videoMemArea = -1; 183 } 184 185 TRACE("leave MapDevice(); result: %ld\n", si.regsArea); 186 return si.regsArea; 187 } 188 189 190 static void 191 UnmapDevice(DeviceInfo& di) 192 { 193 SharedInfo& si = *(di.sharedInfo); 194 195 if (si.regsArea >= 0) 196 delete_area(si.regsArea); 197 if (si.videoMemArea >= 0) 198 delete_area(si.videoMemArea); 199 200 si.regsArea = si.videoMemArea = -1; 201 si.videoMemAddr = (addr_t)NULL; 202 di.regs = NULL; 203 } 204 205 206 static status_t 207 InitDevice(DeviceInfo& di) 208 { 209 // Perform initialization and mapping of the device, and return B_OK if 210 // sucessful; else, return error code. 211 212 // Create the area for shared info with NO user-space read or write 213 // permissions, to prevent accidental damage. 214 215 TRACE("enter InitDevice()\n"); 216 217 size_t sharedSize = (sizeof(SharedInfo) + 7) & ~7; 218 219 di.sharedArea = create_area("3DFX shared info", 220 (void**) &(di.sharedInfo), 221 B_ANY_KERNEL_ADDRESS, 222 ROUND_TO_PAGE_SIZE(sharedSize), 223 B_FULL_LOCK, 224 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_CLONEABLE_AREA); 225 if (di.sharedArea < 0) 226 return di.sharedArea; // return error code 227 228 SharedInfo& si = *(di.sharedInfo); 229 230 memset(&si, 0, sharedSize); 231 232 pci_info& pciInfo = di.pciInfo; 233 234 si.vendorID = pciInfo.vendor_id; 235 si.deviceID = pciInfo.device_id; 236 si.revision = pciInfo.revision; 237 si.chipType = di.pChipInfo->chipType; 238 strcpy(si.chipName, di.pChipInfo->chipName); 239 240 status_t status = MapDevice(di); 241 if (status < 0) { 242 delete_area(di.sharedArea); 243 di.sharedArea = -1; 244 di.sharedInfo = NULL; 245 return status; // return error code 246 } 247 248 return B_OK; 249 } 250 251 252 static const ChipInfo* 253 GetNextSupportedDevice(uint32& pciIndex, pci_info& pciInfo) 254 { 255 // Search the PCI devices for a device that is supported by this driver. 256 // The search starts at the device specified by argument pciIndex, and 257 // continues until a supported device is found or there are no more devices 258 // to examine. Argument pciIndex is incremented after each device is 259 // examined. 260 261 // If a supported device is found, return a pointer to the struct containing 262 // the chip info; else return NULL. 263 264 while (gPCI->get_nth_pci_info(pciIndex, &pciInfo) == B_OK) { 265 266 if (pciInfo.vendor_id == VENDOR_ID) { 267 268 // Search the table of supported devices to find a chip/device that 269 // matches device ID of the current PCI device. 270 271 const ChipInfo* pDevice = chipTable; 272 273 while (pDevice->chipID != 0) { // end of table? 274 if (pDevice->chipID == pciInfo.device_id) 275 return pDevice; // matching device/chip found 276 277 pDevice++; 278 } 279 } 280 281 pciIndex++; 282 } 283 284 return NULL; // no supported device found 285 } 286 287 288 289 // #pragma mark - Kernel Interface 290 291 292 status_t 293 init_hardware(void) 294 { 295 // Return B_OK if a device supported by this driver is found; otherwise, 296 // return B_ERROR so the driver will be unloaded. 297 298 if (get_module(B_PCI_MODULE_NAME, (module_info**)&gPCI) != B_OK) 299 return B_ERROR; // unable to access PCI bus 300 301 // Check pci devices for a device supported by this driver. 302 303 uint32 pciIndex = 0; 304 pci_info pciInfo; 305 const ChipInfo* pDevice = GetNextSupportedDevice(pciIndex, pciInfo); 306 307 TRACE("init_hardware() - %s\n", 308 pDevice == NULL ? "no supported devices" : "device supported"); 309 310 put_module(B_PCI_MODULE_NAME); // put away the module manager 311 312 return (pDevice == NULL ? B_ERROR : B_OK); 313 } 314 315 316 status_t 317 init_driver(void) 318 { 319 // Get handle for the pci bus. 320 321 if (get_module(B_PCI_MODULE_NAME, (module_info**)&gPCI) != B_OK) 322 return B_ERROR; 323 324 status_t status = gLock.Init("3DFX driver lock"); 325 if (status < B_OK) 326 return status; 327 328 // Get info about all the devices supported by this driver. 329 330 uint32 pciIndex = 0; 331 uint32 count = 0; 332 333 while (count < MAX_DEVICES) { 334 DeviceInfo& di = gDeviceInfo[count]; 335 336 const ChipInfo* pDevice = GetNextSupportedDevice(pciIndex, di.pciInfo); 337 if (pDevice == NULL) 338 break; // all supported devices have been obtained 339 340 // Compose device name. 341 sprintf(di.name, "graphics/" DEVICE_FORMAT, 342 di.pciInfo.vendor_id, di.pciInfo.device_id, 343 di.pciInfo.bus, di.pciInfo.device, di.pciInfo.function); 344 TRACE("init_driver() match found; name: %s\n", di.name); 345 346 gDeviceNames[count] = di.name; 347 di.openCount = 0; // mark driver as available for R/W open 348 di.sharedArea = -1; // indicate shared area not yet created 349 di.sharedInfo = NULL; 350 di.pChipInfo = pDevice; 351 count++; 352 pciIndex++; 353 } 354 355 gDeviceNames[count] = NULL; // terminate list with null pointer 356 357 TRACE("init_driver() %ld supported devices\n", count); 358 359 return B_OK; 360 } 361 362 363 void 364 uninit_driver(void) 365 { 366 // Free the driver data. 367 368 gLock.Delete(); 369 put_module(B_PCI_MODULE_NAME); // put the pci module away 370 } 371 372 373 const char** 374 publish_devices(void) 375 { 376 return (const char**)gDeviceNames; // return list of supported devices 377 } 378 379 380 device_hooks* 381 find_device(const char* name) 382 { 383 int i = 0; 384 while (gDeviceNames[i] != NULL) { 385 if (strcmp(name, gDeviceNames[i]) == 0) 386 return &gDeviceHooks; 387 i++; 388 } 389 390 return NULL; 391 } 392 393 394 395 // #pragma mark - Device Hooks 396 397 398 static status_t 399 device_open(const char* name, uint32 /*flags*/, void** cookie) 400 { 401 status_t status = B_OK; 402 403 TRACE("device_open() - name: %s, cookie: 0x%08lx)\n", name, (uint32)cookie); 404 405 // Find the device name in the list of devices. 406 407 int32 i = 0; 408 while (gDeviceNames[i] != NULL && (strcmp(name, gDeviceNames[i]) != 0)) 409 i++; 410 411 if (gDeviceNames[i] == NULL) 412 return B_BAD_VALUE; // device name not found in list of devices 413 414 DeviceInfo& di = gDeviceInfo[i]; 415 416 gLock.Acquire(); // make sure no one else has write access to common data 417 418 if (di.openCount == 0) 419 status = InitDevice(di); 420 421 gLock.Release(); 422 423 if (status == B_OK) { 424 di.openCount++; // mark device open 425 *cookie = &di; // send cookie to opener 426 } 427 428 TRACE("device_open() returning 0x%lx, open count: %ld\n", status, 429 di.openCount); 430 return status; 431 } 432 433 434 static status_t 435 device_read(void* dev, off_t pos, void* buf, size_t* len) 436 { 437 // Following 3 lines of code are here to eliminate "unused parameter" warnings. 438 (void)dev; 439 (void)pos; 440 (void)buf; 441 442 *len = 0; 443 return B_NOT_ALLOWED; 444 } 445 446 447 static status_t 448 device_write(void* dev, off_t pos, const void* buf, size_t* len) 449 { 450 // Following 3 lines of code are here to eliminate "unused parameter" warnings. 451 (void)dev; 452 (void)pos; 453 (void)buf; 454 455 *len = 0; 456 return B_NOT_ALLOWED; 457 } 458 459 460 static status_t 461 device_close(void* dev) 462 { 463 (void)dev; // avoid compiler warning for unused arg 464 465 TRACE("device_close()\n"); 466 return B_NO_ERROR; 467 } 468 469 470 static status_t 471 device_free(void* dev) 472 { 473 DeviceInfo& di = *((DeviceInfo*)dev); 474 475 TRACE("enter device_free()\n"); 476 477 gLock.Acquire(); // lock driver 478 479 // If opened multiple times, merely decrement the open count and exit. 480 481 if (di.openCount <= 1) { 482 UnmapDevice(di); // free regs and frame buffer areas 483 484 delete_area(di.sharedArea); 485 di.sharedArea = -1; 486 di.sharedInfo = NULL; 487 } 488 489 if (di.openCount > 0) 490 di.openCount--; // mark device available 491 492 gLock.Release(); // unlock driver 493 494 TRACE("exit device_free() openCount: %ld\n", di.openCount); 495 return B_OK; 496 } 497 498 499 static status_t 500 device_ioctl(void* dev, uint32 msg, void* buffer, size_t bufferLength) 501 { 502 DeviceInfo& di = *((DeviceInfo*)dev); 503 504 #ifndef __HAIKU__ 505 (void)bufferLength; // avoid compiler warning for unused arg 506 #endif 507 508 switch (msg) { 509 case B_GET_ACCELERANT_SIGNATURE: 510 strcpy((char*)buffer, ACCELERANT_NAME); 511 return B_OK; 512 513 case TDFX_DEVICE_NAME: 514 strncpy((char*)buffer, di.name, B_OS_NAME_LENGTH); 515 ((char*)buffer)[B_OS_NAME_LENGTH -1] = '\0'; 516 return B_OK; 517 518 case TDFX_GET_SHARED_DATA: 519 #ifdef __HAIKU__ 520 if (bufferLength != sizeof(area_id)) 521 return B_BAD_DATA; 522 #endif 523 524 *((area_id*)buffer) = di.sharedArea; 525 return B_OK; 526 527 case TDFX_GET_PIO_REG: 528 { 529 #ifdef __HAIKU__ 530 if (bufferLength != sizeof(PIORegInfo)) 531 return B_BAD_DATA; 532 #endif 533 534 PIORegInfo* regInfo = (PIORegInfo*)buffer; 535 if (regInfo->magic == TDFX_PRIVATE_DATA_MAGIC) { 536 int ioAddr = di.pciInfo.u.h0.base_registers[2] + regInfo->offset; 537 if (regInfo->index >= 0) { 538 gPCI->write_io_8(ioAddr, regInfo->index); 539 regInfo->value = gPCI->read_io_8(ioAddr + 1); 540 } else { 541 regInfo->value = gPCI->read_io_8(ioAddr); 542 } 543 return B_OK; 544 } 545 break; 546 } 547 548 case TDFX_SET_PIO_REG: 549 { 550 #ifdef __HAIKU__ 551 if (bufferLength != sizeof(PIORegInfo)) 552 return B_BAD_DATA; 553 #endif 554 555 PIORegInfo* regInfo = (PIORegInfo*)buffer; 556 if (regInfo->magic == TDFX_PRIVATE_DATA_MAGIC) { 557 int ioAddr = di.pciInfo.u.h0.base_registers[2] + regInfo->offset; 558 if (regInfo->index >= 0) { 559 gPCI->write_io_8(ioAddr, regInfo->index); 560 gPCI->write_io_8(ioAddr + 1, regInfo->value); 561 } else { 562 gPCI->write_io_8(ioAddr, regInfo->value); 563 } 564 return B_OK; 565 } 566 break; 567 } 568 } 569 570 return B_DEV_INVALID_IOCTL; 571 } 572