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