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