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