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