1 /*- 2 * Copyright (c) 2000-2001 by Coleman Kane <cokane@FreeBSD.org> 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 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by Gardner Buchanan. 16 * 4. The name of Gardner Buchanan may not be used to endorse or promote 17 * products derived from this software without specific prior written 18 * permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __FBSDID("$FreeBSD$"); 34 35 /* 3dfx driver for FreeBSD 4.x - Finished 11 May 2000, 12:25AM ET 36 * 37 * Copyright (C) 2000-2001, by Coleman Kane <cokane@FreeBSD.org>, 38 * based upon the 3dfx driver written for linux, by Daryll Straus, Jon Taylor, 39 * and Jens Axboe, located at http://linux.3dfx.com. 40 */ 41 42 #include <sys/param.h> 43 44 #include <sys/bus.h> 45 #include <sys/cdefs.h> 46 #include <sys/conf.h> 47 #include <sys/fcntl.h> 48 #include <sys/file.h> 49 #include <sys/filedesc.h> 50 #include <sys/filio.h> 51 #include <sys/ioccom.h> 52 #include <sys/kernel.h> 53 #include <sys/malloc.h> 54 #include <sys/mman.h> 55 #include <sys/signalvar.h> 56 #include <sys/systm.h> 57 #include <sys/uio.h> 58 59 #include <dev/pci/pcivar.h> 60 #include <dev/pci/pcireg.h> 61 62 #include <vm/vm.h> 63 #include <vm/vm_kern.h> 64 #include <vm/pmap.h> 65 #include <vm/vm_extern.h> 66 67 /* rman.h depends on machine/bus.h */ 68 #include <machine/resource.h> 69 #include <machine/bus.h> 70 #include <sys/rman.h> 71 72 /* This must come first */ 73 #include "opt_tdfx.h" 74 #ifdef TDFX_LINUX 75 #include <dev/tdfx/tdfx_linux.h> 76 #endif 77 78 #include <dev/tdfx/tdfx_io.h> 79 #include <dev/tdfx/tdfx_vars.h> 80 #include <dev/tdfx/tdfx_pci.h> 81 82 83 static devclass_t tdfx_devclass; 84 85 86 static int tdfx_count = 0; 87 88 89 /* Set up the boot probe/attach routines */ 90 static device_method_t tdfx_methods[] = { 91 DEVMETHOD(device_probe, tdfx_probe), 92 DEVMETHOD(device_attach, tdfx_attach), 93 DEVMETHOD(device_detach, tdfx_detach), 94 DEVMETHOD(device_shutdown, tdfx_shutdown), 95 { 0, 0 } 96 }; 97 98 MALLOC_DEFINE(M_TDFX,"TDFX Driver","3DFX Graphics[/2D]/3D Accelerator(s)"); 99 100 #ifdef TDFX_LINUX 101 MODULE_DEPEND(tdfx, linux, 1, 1, 1); 102 LINUX_IOCTL_SET(tdfx, LINUX_IOCTL_TDFX_MIN, LINUX_IOCTL_TDFX_MAX); 103 #endif 104 105 /* Char. Dev. file operations structure */ 106 static struct cdevsw tdfx_cdev = { 107 .d_version = D_VERSION, 108 .d_flags = D_NEEDGIANT, 109 .d_open = tdfx_open, 110 .d_close = tdfx_close, 111 .d_ioctl = tdfx_ioctl, 112 .d_mmap = tdfx_mmap, 113 .d_name = "tdfx", 114 }; 115 116 static int 117 tdfx_probe(device_t dev) 118 { 119 /* 120 * probe routine called on kernel boot to register supported devices. We get 121 * a device structure to work with, and we can test the VENDOR/DEVICE IDs to 122 * see if this PCI device is one that we support. Return 0 if yes, ENXIO if 123 * not. 124 */ 125 switch(pci_get_devid(dev)) { 126 case PCI_DEVICE_ALLIANCE_AT3D: 127 device_set_desc(dev, "ProMotion At3D 3D Accelerator"); 128 return 0; 129 case PCI_DEVICE_3DFX_VOODOO2: 130 device_set_desc(dev, "3DFX Voodoo II 3D Accelerator"); 131 return 0; 132 /*case PCI_DEVICE_3DFX_BANSHEE: 133 device_set_desc(dev, "3DFX Voodoo Banshee 2D/3D Graphics Accelerator"); 134 return 0; 135 case PCI_DEVICE_3DFX_VOODOO3: 136 device_set_desc(dev, "3DFX Voodoo3 2D/3D Graphics Accelerator"); 137 return 0;*/ 138 case PCI_DEVICE_3DFX_VOODOO1: 139 device_set_desc(dev, "3DFX Voodoo Graphics 3D Accelerator"); 140 return 0;; 141 }; 142 143 return ENXIO; 144 } 145 146 static int 147 tdfx_attach(device_t dev) { 148 /* 149 * The attach routine is called after the probe routine successfully says it 150 * supports a given card. We now proceed to initialize this card for use with 151 * the system. I want to map the device memory for userland allocation and 152 * fill an information structure with information on this card. I'd also like 153 * to set Write Combining with the MTRR code so that we can hopefully speed 154 * up memory writes. The last thing is to register the character device 155 * interface to the card, so we can open it from /dev/3dfxN, where N is a 156 * small, whole number. 157 */ 158 struct tdfx_softc *tdfx_info; 159 u_long val; 160 /* rid value tells bus_alloc_resource where to find the addresses of ports or 161 * of memory ranges in the PCI config space*/ 162 int rid = PCIR_BAR(0); 163 164 /* Increment the card counter (for the ioctl code) */ 165 tdfx_count++; 166 167 /* Enable MemMap on Voodoo */ 168 val = pci_read_config(dev, PCIR_COMMAND, 2); 169 val |= (PCIM_CMD_MEMEN); 170 pci_write_config(dev, PCIR_COMMAND, val, 2); 171 val = pci_read_config(dev, PCIR_COMMAND, 2); 172 173 /* Fill the soft config struct with info about this device*/ 174 tdfx_info = device_get_softc(dev); 175 tdfx_info->dev = dev; 176 tdfx_info->vendor = pci_get_vendor(dev); 177 tdfx_info->type = pci_get_devid(dev) >> 16; 178 tdfx_info->bus = pci_get_bus(dev); 179 tdfx_info->dv = pci_get_slot(dev); 180 tdfx_info->curFile = NULL; 181 182 /* 183 * Get the Memory Location from the PCI Config, mask out lower word, since 184 * the config space register is only one word long (this is nicer than a 185 * bitshift). 186 */ 187 tdfx_info->addr0 = (pci_read_config(dev, 0x10, 4) & 0xffff0000); 188 #ifdef DEBUG 189 device_printf(dev, "Base0 @ 0x%x\n", tdfx_info->addr0); 190 #endif 191 /* Notify the VM that we will be mapping some memory later */ 192 tdfx_info->memrange = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 193 &rid, RF_ACTIVE | RF_SHAREABLE); 194 if(tdfx_info->memrange == NULL) { 195 #ifdef DEBUG 196 device_printf(dev, "Error mapping mem, won't be able to use mmap()\n"); 197 #endif 198 tdfx_info->memrid = 0; 199 } 200 else { 201 tdfx_info->memrid = rid; 202 #ifdef DEBUG 203 device_printf(dev, "Mapped to: 0x%x\n", 204 (unsigned int)rman_get_start(tdfx_info->memrange)); 205 #endif 206 } 207 208 /* Setup for Voodoo3 and Banshee, PIO and an extram Memrange */ 209 if(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3 || 210 pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE) { 211 rid = 0x14; /* 2nd mem map */ 212 tdfx_info->addr1 = (pci_read_config(dev, 0x14, 4) & 0xffff0000); 213 #ifdef DEBUG 214 device_printf(dev, "Base1 @ 0x%x\n", tdfx_info->addr1); 215 #endif 216 tdfx_info->memrange2 = bus_alloc_resource_any(dev, 217 SYS_RES_MEMORY, &rid, RF_ACTIVE | RF_SHAREABLE); 218 if(tdfx_info->memrange2 == NULL) { 219 #ifdef DEBUG 220 device_printf(dev, "Mem1 couldn't be allocated, glide may not work."); 221 #endif 222 tdfx_info->memrid2 = 0; 223 } 224 else { 225 tdfx_info->memrid2 = rid; 226 } 227 /* Now to map the PIO stuff */ 228 rid = PCIR_IOBASE0_2; 229 tdfx_info->pio0 = pci_read_config(dev, 0x2c, 2); 230 tdfx_info->pio0max = pci_read_config(dev, 0x30, 2) + tdfx_info->pio0; 231 tdfx_info->piorange = bus_alloc_resource_any(dev, 232 SYS_RES_IOPORT, &rid, RF_ACTIVE | RF_SHAREABLE); 233 if(tdfx_info->piorange == NULL) { 234 #ifdef DEBUG 235 device_printf(dev, "Couldn't map PIO range."); 236 #endif 237 tdfx_info->piorid = 0; 238 } 239 else { 240 tdfx_info->piorid = rid; 241 } 242 } else { 243 tdfx_info->addr1 = 0; 244 tdfx_info->memrange2 = NULL; 245 tdfx_info->piorange = NULL; 246 } 247 248 /* 249 * Set Writecombining, or at least Uncacheable for the memory region, if we 250 * are able to 251 */ 252 253 if(tdfx_setmtrr(dev) != 0) { 254 #ifdef DEBUG 255 device_printf(dev, "Some weird error setting MTRRs"); 256 #endif 257 return -1; 258 } 259 260 /* 261 * make_dev registers the cdev to access the 3dfx card from /dev 262 * use hex here for the dev num, simply to provide better support if > 10 263 * voodoo cards, for the mad. The user must set the link, or use MAKEDEV. 264 * Why would we want that many voodoo cards anyhow? 265 */ 266 tdfx_info->devt = make_dev(&tdfx_cdev, device_get_unit(dev), 267 UID_ROOT, GID_WHEEL, 0600, "3dfx%x", device_get_unit(dev)); 268 269 return 0; 270 } 271 272 static int 273 tdfx_detach(device_t dev) { 274 struct tdfx_softc* tdfx_info; 275 int retval; 276 tdfx_info = device_get_softc(dev); 277 278 /* Delete allocated resource, of course */ 279 bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid, 280 tdfx_info->memrange); 281 282 /* Release extended Voodoo3/Banshee resources */ 283 if(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE || 284 pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) { 285 if(tdfx_info->memrange2 != NULL) 286 bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid2, 287 tdfx_info->memrange); 288 /* if(tdfx_info->piorange != NULL) 289 bus_release_resource(dev, SYS_RES_IOPORT, tdfx_info->piorid, 290 tdfx_info->piorange);*/ 291 } 292 293 /* Though it is safe to leave the WRCOMB support since the 294 mem driver checks for it, we should remove it in order 295 to free an MTRR for another device */ 296 retval = tdfx_clrmtrr(dev); 297 #ifdef DEBUG 298 if(retval != 0) 299 printf("tdfx: For some reason, I couldn't clear the mtrr\n"); 300 #endif 301 /* Remove device entry when it can no longer be accessed */ 302 destroy_dev(tdfx_info->devt); 303 return(0); 304 } 305 306 static int 307 tdfx_shutdown(device_t dev) { 308 #ifdef DEBUG 309 device_printf(dev, "tdfx: Device Shutdown\n"); 310 #endif 311 return 0; 312 } 313 314 static int 315 tdfx_clrmtrr(device_t dev) { 316 /* This function removes the MTRR set by the attach call, so it can be used 317 * in the future by other drivers. 318 */ 319 int retval, act; 320 struct tdfx_softc *tdfx_info = device_get_softc(dev); 321 322 act = MEMRANGE_SET_REMOVE; 323 retval = mem_range_attr_set(&tdfx_info->mrdesc, &act); 324 return retval; 325 } 326 327 static int 328 tdfx_setmtrr(device_t dev) { 329 /* 330 * This is the MTRR setting function for the 3dfx card. It is called from 331 * tdfx_attach. If we can't set the MTRR properly, it's not the end of the 332 * world. We can still continue, just with slightly (very slightly) degraded 333 * performance. 334 */ 335 int retval = 0, act; 336 struct tdfx_softc *tdfx_info = device_get_softc(dev); 337 338 /* The older Voodoo cards have a shorter memrange than the newer ones */ 339 if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1) || (pci_get_devid(dev) == 340 PCI_DEVICE_3DFX_VOODOO2)) { 341 tdfx_info->mrdesc.mr_len = 0x400000; 342 343 /* The memory descriptor is described as the top 15 bits of the real 344 address */ 345 tdfx_info->mrdesc.mr_base = tdfx_info->addr0 & 0xfffe0000; 346 } 347 else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) || 348 (pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE)) { 349 tdfx_info->mrdesc.mr_len = 0x1000000; 350 /* The Voodoo3 and Banshee LFB is the second memory address */ 351 /* The memory descriptor is described as the top 15 bits of the real 352 address */ 353 tdfx_info->mrdesc.mr_base = tdfx_info->addr1 & 0xfffe0000; 354 } 355 else 356 return 0; 357 /* 358 * The Alliance Pro Motion AT3D was not mentioned in the linux 359 * driver as far as MTRR support goes, so I just won't put the 360 * code in here for it. This is where it should go, though. 361 */ 362 363 /* Firstly, try to set write combining */ 364 tdfx_info->mrdesc.mr_flags = MDF_WRITECOMBINE; 365 bcopy("tdfx", &tdfx_info->mrdesc.mr_owner, 4); 366 act = MEMRANGE_SET_UPDATE; 367 retval = mem_range_attr_set(&tdfx_info->mrdesc, &act); 368 369 if(retval == 0) { 370 #ifdef DEBUG 371 device_printf(dev, "MTRR Set Correctly for tdfx\n"); 372 #endif 373 } else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO2) || 374 (pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1)) { 375 /* if, for some reason we can't set the WRCOMB range with the V1/V2, we 376 * can still possibly use the UNCACHEABLE region for it instead, and help 377 * out in a small way */ 378 tdfx_info->mrdesc.mr_flags = MDF_UNCACHEABLE; 379 /* This length of 1000h was taken from the linux device driver... */ 380 tdfx_info->mrdesc.mr_len = 0x1000; 381 382 /* 383 * If, for some reason, we can't set the MTRR (N/A?) we may still continue 384 */ 385 #ifdef DEBUG 386 if(retval == 0) { 387 device_printf(dev, "MTRR Set Type Uncacheable %x\n", 388 (u_int32_t)tdfx_info->mrdesc.mr_base); 389 } else { 390 device_printf(dev, "Couldn't Set MTRR\n"); 391 } 392 #endif 393 } 394 #ifdef DEBUG 395 else { 396 device_printf(dev, "Couldn't Set MTRR\n"); 397 return 0; 398 } 399 #endif 400 return 0; 401 } 402 403 static int 404 tdfx_open(dev_t dev, int flags, int fmt, struct thread *td) 405 { 406 /* 407 * The open cdev method handles open(2) calls to /dev/3dfx[n] 408 * We can pretty much allow any opening of the device. 409 */ 410 struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass, 411 UNIT(minor(dev))); 412 if(tdfx_info->busy != 0) return EBUSY; 413 #ifdef DEBUG 414 printf("3dfx: Opened by #%d\n", td->td_proc->p_pid); 415 #endif 416 /* Set the driver as busy */ 417 tdfx_info->busy++; 418 return 0; 419 } 420 421 static int 422 tdfx_close(dev_t dev, int fflag, int devtype, struct thread *td) 423 { 424 /* 425 * The close cdev method handles close(2) calls to /dev/3dfx[n] 426 * We'll always want to close the device when it's called. 427 */ 428 struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass, 429 UNIT(minor(dev))); 430 if(tdfx_info->busy == 0) return EBADF; 431 tdfx_info->busy = 0; 432 #ifdef DEBUG 433 printf("Closed by #%d\n", td->td_proc->p_pid); 434 #endif 435 return 0; 436 } 437 438 static int 439 tdfx_mmap(dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) 440 { 441 /* 442 * mmap(2) is called by a user process to request that an area of memory 443 * associated with this device be mapped for the process to work with. Nprot 444 * holds the protections requested, PROT_READ, PROT_WRITE, or both. 445 */ 446 447 /**** OLD GET CONFIG ****/ 448 /* struct tdfx_softc* tdfx_info; */ 449 450 /* Get the configuration for our card XXX*/ 451 /*tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 452 UNIT(minor(dev)));*/ 453 /************************/ 454 455 struct tdfx_softc* tdfx_info[2]; 456 457 tdfx_info[0] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 0); 458 459 /* If, for some reason, its not configured, we bail out */ 460 if(tdfx_info[0] == NULL) { 461 #ifdef DEBUG 462 printf("tdfx: tdfx_info (softc) is NULL\n"); 463 #endif 464 return -1; 465 } 466 467 /* We must stay within the bound of our address space */ 468 if((offset & 0xff000000) == tdfx_info[0]->addr0) { 469 offset &= 0xffffff; 470 *paddr = rman_get_start(tdfx_info[0]->memrange) + offset; 471 return 0; 472 } 473 474 if(tdfx_count > 1) { 475 tdfx_info[1] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 1); 476 if((offset & 0xff000000) == tdfx_info[1]->addr0) { 477 offset &= 0xffffff; 478 *paddr = rman_get_start(tdfx_info[1]->memrange) + 479 offset; 480 return 0; 481 } 482 } 483 484 /* See if the Banshee/V3 LFB is being requested */ 485 /*if(tdfx_info->memrange2 != NULL && (offset & 0xff000000) == 486 tdfx_info->addr1) { 487 offset &= 0xffffff; 488 return atop(rman_get_start(tdfx_info[1]->memrange2) + offset); 489 }*/ /* VoodooNG code */ 490 491 /* The ret call */ 492 /* atop -> address to page 493 * rman_get_start, get the (struct resource*)->r_start member, 494 * the mapping base address. 495 */ 496 return -1; 497 } 498 499 static int 500 tdfx_query_boards(void) { 501 /* 502 * This returns the number of installed tdfx cards, we have been keeping 503 * count, look at tdfx_attach 504 */ 505 return tdfx_count; 506 } 507 508 static int 509 tdfx_query_fetch(u_int cmd, struct tdfx_pio_data *piod) 510 { 511 /* XXX Comment this later, after careful inspection and spring cleaning :) */ 512 /* Various return values 8bit-32bit */ 513 u_int8_t ret_byte; 514 u_int16_t ret_word; 515 u_int32_t ret_dword; 516 struct tdfx_softc* tdfx_info = NULL; 517 518 /* This one depend on the tdfx_* structs being properly initialized */ 519 520 /*piod->device &= 0xf;*/ 521 if((piod == NULL) ||(tdfx_count <= piod->device) || 522 (piod->device < 0)) { 523 #ifdef DEBUG 524 printf("tdfx: Bad device or internal struct in tdfx_query_fetch\n"); 525 #endif 526 return -EINVAL; 527 } 528 529 tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 530 piod->device); 531 532 if(tdfx_info == NULL) return -ENXIO; 533 534 /* We must restrict the size reads from the port, since to high or low of a 535 * size witll result in wrong data being passed, and that's bad */ 536 /* A few of these were pulled during the attach phase */ 537 switch(piod->port) { 538 case PCI_VENDOR_ID_FREEBSD: 539 if(piod->size != 2) return -EINVAL; 540 copyout(&tdfx_info->vendor, piod->value, piod->size); 541 return 0; 542 case PCI_DEVICE_ID_FREEBSD: 543 if(piod->size != 2) return -EINVAL; 544 copyout(&tdfx_info->type, piod->value, piod->size); 545 return 0; 546 case PCI_BASE_ADDRESS_0_FREEBSD: 547 if(piod->size != 4) return -EINVAL; 548 copyout(&tdfx_info->addr0, piod->value, piod->size); 549 return 0; 550 case PCI_BASE_ADDRESS_1_FREEBSD: 551 if(piod->size != 4) return -EINVAL; 552 copyout(&tdfx_info->addr1, piod->value, piod->size); 553 return 0; 554 case PCI_PRIBUS_FREEBSD: 555 if(piod->size != 1) return -EINVAL; 556 break; 557 case PCI_IOBASE_0_FREEBSD: 558 if(piod->size != 2) return -EINVAL; 559 break; 560 case PCI_IOLIMIT_0_FREEBSD: 561 if(piod->size != 2) return -EINVAL; 562 break; 563 case SST1_PCI_SPECIAL1_FREEBSD: 564 if(piod->size != 4) return -EINVAL; 565 break; 566 case PCI_REVISION_ID_FREEBSD: 567 if(piod->size != 1) return -EINVAL; 568 break; 569 case SST1_PCI_SPECIAL4_FREEBSD: 570 if(piod->size != 4) return -EINVAL; 571 break; 572 default: 573 return -EINVAL; 574 } 575 576 577 /* Read the value and return */ 578 switch(piod->size) { 579 case 1: 580 ret_byte = pci_read_config(tdfx_info[piod->device].dev, 581 piod->port, 1); 582 copyout(&ret_byte, piod->value, 1); 583 break; 584 case 2: 585 ret_word = pci_read_config(tdfx_info[piod->device].dev, 586 piod->port, 2); 587 copyout(&ret_word, piod->value, 2); 588 break; 589 case 4: 590 ret_dword = pci_read_config(tdfx_info[piod->device].dev, 591 piod->port, 4); 592 copyout(&ret_dword, piod->value, 4); 593 break; 594 default: 595 return -EINVAL; 596 } 597 return 0; 598 } 599 600 static int 601 tdfx_query_update(u_int cmd, struct tdfx_pio_data *piod) 602 { 603 /* XXX Comment this later, after careful inspection and spring cleaning :) */ 604 /* Return vals */ 605 u_int8_t ret_byte; 606 u_int16_t ret_word; 607 u_int32_t ret_dword; 608 609 /* Port vals, mask */ 610 u_int32_t retval, preval, mask; 611 struct tdfx_softc* tdfx_info = NULL; 612 613 614 if((piod == NULL) || (piod->device >= (tdfx_count & 615 0xf))) { 616 #ifdef DEBUG 617 printf("tdfx: Bad struct or device in tdfx_query_update\n"); 618 #endif 619 return -EINVAL; 620 } 621 622 tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 623 piod->device); 624 if(tdfx_info == NULL) return -ENXIO; 625 /* Code below this line in the fuction was taken from the 626 * Linux driver and converted for freebsd. */ 627 628 /* Check the size for all the ports, to make sure stuff doesn't get messed up 629 * by poorly written clients */ 630 631 switch(piod->port) { 632 case PCI_COMMAND_FREEBSD: 633 if(piod->size != 2) return -EINVAL; 634 break; 635 case SST1_PCI_SPECIAL1_FREEBSD: 636 if(piod->size != 4) return -EINVAL; 637 break; 638 case SST1_PCI_SPECIAL2_FREEBSD: 639 if(piod->size != 4) return -EINVAL; 640 break; 641 case SST1_PCI_SPECIAL3_FREEBSD: 642 if(piod->size != 4) return -EINVAL; 643 break; 644 case SST1_PCI_SPECIAL4_FREEBSD: 645 if(piod->size != 4) return -EINVAL; 646 break; 647 default: 648 return -EINVAL; 649 } 650 /* Read the current value */ 651 retval = pci_read_config(tdfx_info->dev, piod->port & ~3, 4); 652 653 /* These set up a mask to use, since apparently they wanted to write 4 bytes 654 * at once to the ports */ 655 switch (piod->size) { 656 case 1: 657 copyin(piod->value, &ret_byte, 1); 658 preval = ret_byte << (8 * (piod->port & 0x3)); 659 mask = 0xff << (8 * (piod->port & 0x3)); 660 break; 661 case 2: 662 copyin(piod->value, &ret_word, 2); 663 preval = ret_word << (8 * (piod->port & 0x3)); 664 mask = 0xffff << (8 * (piod->port & 0x3)); 665 break; 666 case 4: 667 copyin(piod->value, &ret_dword, 4); 668 preval = ret_dword; 669 mask = ~0; 670 break; 671 default: 672 return -EINVAL; 673 } 674 /* Finally, combine the values and write it to the port */ 675 retval = (retval & ~mask) | preval; 676 pci_write_config(tdfx_info->dev, piod->port & ~3, retval, 4); 677 678 return 0; 679 } 680 681 /* For both of these, I added a variable named workport of type u_int so 682 * that I could eliminate the warning about my data type size. The 683 * applications expect the port to be of type short, so I needed to change 684 * this within the function */ 685 static int 686 tdfx_do_pio_rd(struct tdfx_pio_data *piod) 687 { 688 /* Return val */ 689 u_int8_t ret_byte; 690 u_int workport; 691 struct tdfx_softc *tdfx_info = 692 (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device); 693 694 /* Restricts the access of ports other than those we use */ 695 if(((piod->port != VGA_INPUT_STATUS_1C) || (piod->port != SC_INDEX) || 696 (piod->port != SC_DATA) || (piod->port != VGA_MISC_OUTPUT_READ)) && 697 (piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max)) 698 return -EPERM; 699 700 /* All VGA STATUS REGS are byte registers, size should never be > 1 */ 701 if(piod->size != 1) { 702 return -EINVAL; 703 } 704 705 /* Write the data to the intended port */ 706 workport = piod->port; 707 ret_byte = inb(workport); 708 copyout(&ret_byte, piod->value, sizeof(u_int8_t)); 709 return 0; 710 } 711 712 static int 713 tdfx_do_pio_wt(struct tdfx_pio_data *piod) 714 { 715 /* return val */ 716 u_int8_t ret_byte; 717 u_int workport; 718 struct tdfx_softc *tdfx_info = (struct 719 tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device); 720 /* Replace old switch w/ massive if(...) */ 721 /* Restricts the access of ports other than those we use */ 722 if(((piod->port != SC_INDEX) && (piod->port != SC_DATA) && 723 (piod->port != VGA_MISC_OUTPUT_READ)) /* Can't write VGA_ST_1C */ && 724 (piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max)) 725 return -EPERM; 726 727 /* All VGA STATUS REGS are byte registers, size should never be > 1 */ 728 if(piod->size != 1) { 729 return -EINVAL; 730 } 731 732 /* Write the data to the intended port */ 733 copyin(piod->value, &ret_byte, sizeof(u_int8_t)); 734 workport = piod->port; 735 outb(workport, ret_byte); 736 return 0; 737 } 738 739 static int 740 tdfx_do_query(u_int cmd, struct tdfx_pio_data *piod) 741 { 742 /* There are three sub-commands to the query 0x33 */ 743 switch(_IOC_NR(cmd)) { 744 case 2: 745 return tdfx_query_boards(); 746 break; 747 case 3: 748 return tdfx_query_fetch(cmd, piod); 749 break; 750 case 4: 751 return tdfx_query_update(cmd, piod); 752 break; 753 default: 754 /* In case we are thrown a bogus sub-command! */ 755 #ifdef DEBUG 756 printf("Bad Sub-cmd: 0x%x\n", _IOC_NR(cmd)); 757 #endif 758 return -EINVAL; 759 } 760 } 761 762 static int 763 tdfx_do_pio(u_int cmd, struct tdfx_pio_data *piod) 764 { 765 /* Two types of PIO, INPUT and OUTPUT, as the name suggests */ 766 switch(_IOC_DIR(cmd)) { 767 case IOCV_OUT: 768 return tdfx_do_pio_rd(piod); 769 break; 770 case IOCV_IN: 771 return tdfx_do_pio_wt(piod); 772 break; 773 default: 774 return -EINVAL; 775 } 776 } 777 778 /* Calls to ioctl(2) eventually end up here. Unhandled ioctls return an ENXIO, 779 * normally, you would read in the data pointed to by data, then write your 780 * output to it. The ioctl *should* normally return zero if everything is 781 * alright, but 3dfx didn't make it that way... 782 * 783 * For all of the ioctl code, in the event of a real error, 784 * we return -Exxxx rather than simply Exxxx. The reason for this 785 * is that the ioctls actually RET information back to the program 786 * sometimes, rather than filling it in the passed structure. We 787 * want to distinguish errors from useful data, and maintain compatibility. 788 * 789 * There is this portion of the proc struct called p_retval[], we can store a 790 * return value in td->td_retval[0] and place the return value if it is positive 791 * in there, then we can return 0 (good). If the return value is negative, we 792 * can return -retval and the error should be properly handled. 793 */ 794 static int 795 tdfx_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td) 796 { 797 int retval = 0; 798 struct tdfx_pio_data *piod = (struct tdfx_pio_data*)data; 799 #ifdef DEBUG 800 printf("IOCTL'd by #%d, cmd: 0x%x, data: %p\n", td->td_proc->p_pid, (u_int32_t)cmd, 801 piod); 802 #endif 803 switch(_IOC_TYPE(cmd)) { 804 /* Return the real error if negative, or simply stick the valid return 805 * in td->td_retval */ 806 case 0x33: 807 /* The '3'(0x33) type IOCTL is for querying the installed cards */ 808 if((retval = tdfx_do_query(cmd, piod)) > 0) td->td_retval[0] = retval; 809 else return -retval; 810 break; 811 case 0: 812 /* The 0 type IOCTL is for programmed I/O methods */ 813 if((tdfx_do_pio(cmd, piod)) > 0) td->td_retval[0] = retval; 814 else return -retval; 815 break; 816 default: 817 /* Technically, we won't reach this from linux emu, but when glide 818 * finally gets ported, watch out! */ 819 #ifdef DEBUG 820 printf("Bad IOCTL from #%d\n", td->td_proc->p_pid); 821 #endif 822 return ENXIO; 823 } 824 825 return 0; 826 } 827 828 #ifdef TDFX_LINUX 829 /* 830 * Linux emulation IOCTL for /dev/tdfx 831 */ 832 static int 833 linux_ioctl_tdfx(struct thread *td, struct linux_ioctl_args* args) 834 { 835 int error = 0; 836 u_long cmd = args->cmd & 0xffff; 837 838 /* The structure passed to ioctl has two shorts, one int 839 and one void*. */ 840 char d_pio[2*sizeof(short) + sizeof(int) + sizeof(void*)]; 841 842 struct file *fp; 843 844 if ((error = fget(td, args->fd, &fp)) != 0) 845 return (error); 846 /* We simply copy the data and send it right to ioctl */ 847 copyin((caddr_t)args->arg, &d_pio, sizeof(d_pio)); 848 error = fo_ioctl(fp, cmd, (caddr_t)&d_pio, td->td_ucred, td); 849 fdrop(fp, td); 850 return error; 851 } 852 #endif /* TDFX_LINUX */ 853 854 855 /* This is the device driver struct. This is sent to the driver subsystem to 856 * register the method structure and the info strcut space for this particular 857 * instance of the driver. 858 */ 859 static driver_t tdfx_driver = { 860 "tdfx", 861 tdfx_methods, 862 sizeof(struct tdfx_softc), 863 }; 864 865 /* Tell Mr. Kernel about us! */ 866 DRIVER_MODULE(tdfx, pci, tdfx_driver, tdfx_devclass, 0, 0); 867