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