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