The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/tdfx/tdfx_pci.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 50e28184b212660e8a12e5d2fc4f0536


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.