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