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: releng/5.3/sys/dev/tdfx/tdfx_pci.c 130585 2004-06-16 09:47:26Z phk $");
   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 0 if yes, ENXIO if
  124          * 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 0;
  130         case PCI_DEVICE_3DFX_VOODOO2:
  131                 device_set_desc(dev, "3DFX Voodoo II 3D Accelerator");
  132                 return 0;
  133         /*case PCI_DEVICE_3DFX_BANSHEE:
  134                 device_set_desc(dev, "3DFX Voodoo Banshee 2D/3D Graphics Accelerator");
  135                 return 0;
  136         case PCI_DEVICE_3DFX_VOODOO3:
  137                 device_set_desc(dev, "3DFX Voodoo3 2D/3D Graphics Accelerator");
  138                 return 0;*/
  139         case PCI_DEVICE_3DFX_VOODOO1:
  140                 device_set_desc(dev, "3DFX Voodoo Graphics 3D Accelerator");
  141                 return 0;;
  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                 if(retval == 0) {
  388                         device_printf(dev, "MTRR Set Type Uncacheable %x\n",
  389                             (u_int32_t)tdfx_info->mrdesc.mr_base);
  390                 } else {
  391                         device_printf(dev, "Couldn't Set MTRR\n");
  392                 }
  393 #endif
  394         }
  395 #ifdef DEBUG
  396         else {
  397                 device_printf(dev, "Couldn't Set MTRR\n");
  398                 return 0;
  399         }
  400 #endif
  401         return 0;
  402 }
  403                 
  404 static int
  405 tdfx_open(struct cdev *dev, int flags, int fmt, struct thread *td)
  406 {
  407         /* 
  408          *      The open cdev method handles open(2) calls to /dev/3dfx[n] 
  409          * We can pretty much allow any opening of the device.
  410          */
  411         struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass, 
  412                         UNIT(minor(dev)));
  413         if(tdfx_info->busy != 0) return EBUSY;
  414 #ifdef  DEBUG
  415         printf("3dfx: Opened by #%d\n", td->td_proc->p_pid);
  416 #endif
  417         /* Set the driver as busy */
  418         tdfx_info->busy++;
  419         return 0;
  420 }
  421 
  422 static int 
  423 tdfx_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 
  424 {
  425         /* 
  426          *      The close cdev method handles close(2) calls to /dev/3dfx[n] 
  427          * We'll always want to close the device when it's called.
  428          */
  429         struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass, 
  430                 UNIT(minor(dev)));
  431         if(tdfx_info->busy == 0) return EBADF;
  432         tdfx_info->busy = 0;
  433 #ifdef  DEBUG
  434         printf("Closed by #%d\n", td->td_proc->p_pid);
  435 #endif
  436         return 0;
  437 }
  438 
  439 static int
  440 tdfx_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
  441 {
  442         /* 
  443          * mmap(2) is called by a user process to request that an area of memory
  444          * associated with this device be mapped for the process to work with. Nprot
  445          * holds the protections requested, PROT_READ, PROT_WRITE, or both.
  446          */
  447 
  448         /**** OLD GET CONFIG ****/
  449         /* struct tdfx_softc* tdfx_info; */
  450         
  451         /* Get the configuration for our card XXX*/
  452         /*tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
  453                         UNIT(minor(dev)));*/
  454         /************************/
  455 
  456         struct tdfx_softc* tdfx_info[2];
  457         
  458         tdfx_info[0] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 0);
  459 
  460         /* If, for some reason, its not configured, we bail out */
  461         if(tdfx_info[0] == NULL) {
  462 #ifdef  DEBUG
  463            printf("tdfx: tdfx_info (softc) is NULL\n");
  464 #endif
  465            return -1;
  466         }
  467 
  468         /* We must stay within the bound of our address space */
  469         if((offset & 0xff000000) == tdfx_info[0]->addr0) {
  470                 offset &= 0xffffff;
  471                 *paddr = rman_get_start(tdfx_info[0]->memrange) + offset;
  472                 return 0;
  473         }
  474         
  475         if(tdfx_count > 1) {
  476                 tdfx_info[1] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 1);
  477                 if((offset & 0xff000000) == tdfx_info[1]->addr0) {
  478                         offset &= 0xffffff;
  479                         *paddr = rman_get_start(tdfx_info[1]->memrange) +
  480                             offset;
  481                         return 0;
  482                 }
  483         }
  484 
  485         /* See if the Banshee/V3 LFB is being requested */
  486         /*if(tdfx_info->memrange2 != NULL && (offset & 0xff000000) ==
  487                         tdfx_info->addr1) {
  488                 offset &= 0xffffff;
  489                 return atop(rman_get_start(tdfx_info[1]->memrange2) + offset);
  490         }*/ /* VoodooNG code */
  491 
  492         /* The ret call */
  493         /* atop -> address to page
  494          * rman_get_start, get the (struct resource*)->r_start member,
  495          * the mapping base address.
  496          */
  497         return -1;
  498 }
  499 
  500 static int
  501 tdfx_query_boards(void) {
  502         /* 
  503     *   This returns the number of installed tdfx cards, we have been keeping
  504          * count, look at tdfx_attach 
  505          */
  506         return tdfx_count;
  507 }
  508 
  509 static int
  510 tdfx_query_fetch(u_int cmd, struct tdfx_pio_data *piod)
  511 {
  512         /* XXX Comment this later, after careful inspection and spring cleaning :) */
  513         /* Various return values 8bit-32bit */
  514         u_int8_t  ret_byte;
  515         u_int16_t ret_word;
  516         u_int32_t ret_dword;
  517         struct tdfx_softc* tdfx_info = NULL;    
  518 
  519         /* This one depend on the tdfx_* structs being properly initialized */
  520 
  521         /*piod->device &= 0xf;*/
  522         if((piod == NULL) ||(tdfx_count <= piod->device) ||
  523                         (piod->device < 0)) {
  524 #ifdef DEBUG
  525                 printf("tdfx: Bad device or internal struct in tdfx_query_fetch\n");
  526 #endif
  527                 return -EINVAL;
  528         }
  529 
  530         tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
  531                         piod->device);
  532 
  533         if(tdfx_info == NULL) return -ENXIO;
  534 
  535         /* We must restrict the size reads from the port, since to high or low of a
  536          * size witll result in wrong data being passed, and that's bad */
  537         /* A few of these were pulled during the attach phase */
  538         switch(piod->port) {
  539                 case PCI_VENDOR_ID_FREEBSD:
  540                         if(piod->size != 2) return -EINVAL;
  541                         copyout(&tdfx_info->vendor, piod->value, piod->size);
  542                         return 0;
  543                 case PCI_DEVICE_ID_FREEBSD:
  544                         if(piod->size != 2) return -EINVAL;
  545                         copyout(&tdfx_info->type, piod->value, piod->size);
  546                         return 0;
  547                 case PCI_BASE_ADDRESS_0_FREEBSD:
  548                         if(piod->size != 4) return -EINVAL;
  549                         copyout(&tdfx_info->addr0, piod->value, piod->size);
  550                         return 0;
  551                 case PCI_BASE_ADDRESS_1_FREEBSD:
  552                         if(piod->size != 4) return -EINVAL;
  553                         copyout(&tdfx_info->addr1, piod->value, piod->size);
  554                         return 0;
  555                 case PCI_PRIBUS_FREEBSD:
  556                         if(piod->size != 1) return -EINVAL;
  557                         break;
  558                 case PCI_IOBASE_0_FREEBSD:
  559                         if(piod->size != 2) return -EINVAL;
  560                         break;
  561                 case PCI_IOLIMIT_0_FREEBSD:
  562                         if(piod->size != 2) return -EINVAL;
  563                         break;
  564                 case SST1_PCI_SPECIAL1_FREEBSD:
  565                         if(piod->size != 4) return -EINVAL;
  566                         break;
  567                 case PCI_REVISION_ID_FREEBSD:
  568                         if(piod->size != 1) return -EINVAL;
  569                         break;
  570                 case SST1_PCI_SPECIAL4_FREEBSD:
  571                         if(piod->size != 4) return -EINVAL;
  572                         break;
  573                 default:
  574                         return -EINVAL;
  575         }
  576 
  577         
  578         /* Read the value and return */
  579         switch(piod->size) {
  580                 case 1:
  581                         ret_byte = pci_read_config(tdfx_info[piod->device].dev, 
  582                                         piod->port, 1);
  583                         copyout(&ret_byte, piod->value, 1);
  584                         break;
  585                 case 2:
  586                         ret_word = pci_read_config(tdfx_info[piod->device].dev, 
  587                                         piod->port, 2);
  588                         copyout(&ret_word, piod->value, 2);
  589                         break;
  590                 case 4:
  591                         ret_dword = pci_read_config(tdfx_info[piod->device].dev, 
  592                                         piod->port, 4);
  593                         copyout(&ret_dword, piod->value, 4);
  594                         break;
  595                 default:
  596                         return -EINVAL;
  597         }
  598         return 0;
  599 }
  600 
  601 static int
  602 tdfx_query_update(u_int cmd, struct tdfx_pio_data *piod)
  603 {
  604         /* XXX Comment this later, after careful inspection and spring cleaning :) */
  605         /* Return vals */
  606         u_int8_t  ret_byte;
  607         u_int16_t ret_word;
  608         u_int32_t ret_dword;
  609 
  610         /* Port vals, mask */
  611         u_int32_t retval, preval, mask;
  612         struct tdfx_softc* tdfx_info = NULL;
  613                         
  614 
  615         if((piod == NULL) || (piod->device >= (tdfx_count &
  616                                         0xf))) {
  617 #ifdef DEBUG
  618                 printf("tdfx: Bad struct or device in tdfx_query_update\n");
  619 #endif
  620                 return -EINVAL;
  621         }
  622 
  623         tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 
  624                         piod->device);
  625         if(tdfx_info == NULL) return -ENXIO;
  626         /* Code below this line in the fuction was taken from the 
  627          * Linux driver and converted for freebsd. */
  628 
  629         /* Check the size for all the ports, to make sure stuff doesn't get messed up
  630          * by poorly written clients */
  631 
  632         switch(piod->port) {
  633                 case PCI_COMMAND_FREEBSD:
  634                         if(piod->size != 2) return -EINVAL;
  635                         break;
  636                 case SST1_PCI_SPECIAL1_FREEBSD:
  637                         if(piod->size != 4) return -EINVAL;
  638                         break;
  639                 case SST1_PCI_SPECIAL2_FREEBSD:
  640                         if(piod->size != 4) return -EINVAL;
  641                         break;
  642                 case SST1_PCI_SPECIAL3_FREEBSD:
  643                         if(piod->size != 4) return -EINVAL;
  644                         break;
  645                 case SST1_PCI_SPECIAL4_FREEBSD:
  646                         if(piod->size != 4) return -EINVAL;
  647                         break;
  648                 default:
  649                         return -EINVAL;
  650         }
  651         /* Read the current value */
  652         retval = pci_read_config(tdfx_info->dev, piod->port & ~3, 4);
  653                         
  654         /* These set up a mask to use, since apparently they wanted to write 4 bytes
  655          * at once to the ports */
  656         switch (piod->size) {
  657                 case 1:
  658                         copyin(piod->value, &ret_byte, 1);
  659                         preval = ret_byte << (8 * (piod->port & 0x3));
  660                         mask = 0xff << (8 * (piod->port & 0x3));
  661                         break;
  662                 case 2:
  663                         copyin(piod->value, &ret_word, 2);
  664                         preval = ret_word << (8 * (piod->port & 0x3));
  665                         mask = 0xffff << (8 * (piod->port & 0x3));
  666                         break;
  667                 case 4:
  668                         copyin(piod->value, &ret_dword, 4);
  669                         preval = ret_dword;
  670                         mask = ~0;
  671                         break;
  672                 default:
  673                         return -EINVAL;
  674         }
  675         /* Finally, combine the values and write it to the port */
  676         retval = (retval & ~mask) | preval;
  677         pci_write_config(tdfx_info->dev, piod->port & ~3, retval, 4);
  678    
  679         return 0;
  680 }
  681 
  682 /* For both of these, I added a variable named workport of type u_int so
  683  * that I could eliminate the warning about my data type size. The
  684  * applications expect the port to be of type short, so I needed to change
  685  * this within the function */
  686 static int
  687 tdfx_do_pio_rd(struct tdfx_pio_data *piod)
  688 {
  689         /* Return val */
  690         u_int8_t  ret_byte;
  691         u_int    workport;
  692         struct tdfx_softc *tdfx_info = 
  693                 (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device);
  694                 
  695         /* Restricts the access of ports other than those we use */
  696         if(((piod->port != VGA_INPUT_STATUS_1C) || (piod->port != SC_INDEX) ||
  697                 (piod->port != SC_DATA) || (piod->port != VGA_MISC_OUTPUT_READ)) &&
  698                 (piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max))
  699                 return -EPERM;
  700         
  701         /* All VGA STATUS REGS are byte registers, size should never be > 1 */
  702         if(piod->size != 1) {
  703                 return -EINVAL;
  704         }
  705 
  706         /* Write the data to the intended port */
  707         workport = piod->port;
  708         ret_byte = inb(workport);
  709         copyout(&ret_byte, piod->value, sizeof(u_int8_t));
  710         return 0;
  711 }
  712 
  713 static int
  714 tdfx_do_pio_wt(struct tdfx_pio_data *piod) 
  715 {
  716         /* return val */
  717         u_int8_t  ret_byte;
  718         u_int            workport;
  719         struct tdfx_softc *tdfx_info = (struct
  720                         tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device);
  721         /* Replace old switch w/ massive if(...) */
  722         /* Restricts the access of ports other than those we use */
  723         if(((piod->port != SC_INDEX) && (piod->port != SC_DATA) && 
  724                 (piod->port != VGA_MISC_OUTPUT_READ)) /* Can't write VGA_ST_1C */ &&
  725                 (piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max))
  726                 return -EPERM;
  727         
  728         /* All VGA STATUS REGS are byte registers, size should never be > 1 */
  729         if(piod->size != 1) {
  730                 return -EINVAL;
  731         }
  732 
  733         /* Write the data to the intended port */
  734         copyin(piod->value, &ret_byte, sizeof(u_int8_t));
  735         workport = piod->port;
  736         outb(workport, ret_byte);
  737         return 0;
  738 }
  739 
  740 static int
  741 tdfx_do_query(u_int cmd, struct tdfx_pio_data *piod)
  742 {
  743         /* There are three sub-commands to the query 0x33 */
  744         switch(_IOC_NR(cmd)) {
  745                 case 2:
  746                         return tdfx_query_boards();
  747                         break;
  748                 case 3:
  749                         return tdfx_query_fetch(cmd, piod);
  750                         break;
  751                 case 4:
  752                         return tdfx_query_update(cmd, piod);
  753                         break;
  754                 default:
  755                         /* In case we are thrown a bogus sub-command! */
  756 #ifdef DEBUG
  757                         printf("Bad Sub-cmd: 0x%x\n", _IOC_NR(cmd));
  758 #endif
  759                         return -EINVAL;
  760         }
  761 }
  762 
  763 static int
  764 tdfx_do_pio(u_int cmd, struct tdfx_pio_data *piod) 
  765 {
  766         /* Two types of PIO, INPUT and OUTPUT, as the name suggests */
  767         switch(_IOC_DIR(cmd)) {
  768                 case IOCV_OUT: 
  769                         return tdfx_do_pio_rd(piod);
  770                         break;
  771                 case IOCV_IN:
  772                         return tdfx_do_pio_wt(piod);
  773                         break;
  774                 default:
  775                         return -EINVAL;
  776         }
  777 }
  778 
  779 /* Calls to ioctl(2) eventually end up here. Unhandled ioctls return an ENXIO,
  780  * normally, you would read in the data pointed to by data, then write your
  781  * output to it. The ioctl *should* normally return zero if everything is
  782  * alright, but 3dfx didn't make it that way...
  783  *
  784  * For all of the ioctl code, in the event of a real error,
  785  * we return -Exxxx rather than simply Exxxx. The reason for this
  786  * is that the ioctls actually RET information back to the program
  787  * sometimes, rather than filling it in the passed structure. We
  788  * want to distinguish errors from useful data, and maintain compatibility.
  789  *
  790  * There is this portion of the proc struct called p_retval[], we can store a
  791  * return value in td->td_retval[0] and place the return value if it is positive
  792  * in there, then we can return 0 (good). If the return value is negative, we
  793  * can return -retval and the error should be properly handled.
  794  */
  795 static int
  796 tdfx_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
  797 {
  798         int retval = 0;
  799         struct tdfx_pio_data *piod = (struct tdfx_pio_data*)data;
  800 #ifdef  DEBUG
  801         printf("IOCTL'd by #%d, cmd: 0x%x, data: %p\n", td->td_proc->p_pid, (u_int32_t)cmd,
  802                         piod);
  803 #endif
  804         switch(_IOC_TYPE(cmd)) {
  805                 /* Return the real error if negative, or simply stick the valid return
  806                  * in td->td_retval */
  807         case 0x33:
  808                         /* The '3'(0x33) type IOCTL is for querying the installed cards */
  809                         if((retval = tdfx_do_query(cmd, piod)) > 0) td->td_retval[0] = retval;
  810                         else return -retval;
  811                         break;
  812                 case 0:
  813                         /* The 0 type IOCTL is for programmed I/O methods */
  814                         if((tdfx_do_pio(cmd, piod)) > 0) td->td_retval[0] = retval;
  815                         else return -retval;
  816                         break;
  817                 default:
  818                         /* Technically, we won't reach this from linux emu, but when glide
  819                          * finally gets ported, watch out! */
  820 #ifdef DEBUG
  821                         printf("Bad IOCTL from #%d\n", td->td_proc->p_pid);
  822 #endif
  823                         return ENXIO;
  824         }
  825 
  826         return 0;
  827 }
  828 
  829 #ifdef TDFX_LINUX
  830 /*
  831  * Linux emulation IOCTL for /dev/tdfx
  832  */
  833 static int
  834 linux_ioctl_tdfx(struct thread *td, struct linux_ioctl_args* args)
  835 {
  836    int error = 0;
  837    u_long cmd = args->cmd & 0xffff;
  838 
  839    /* The structure passed to ioctl has two shorts, one int
  840       and one void*. */
  841    char d_pio[2*sizeof(short) + sizeof(int) + sizeof(void*)];
  842 
  843    struct file *fp;
  844 
  845    if ((error = fget(td, args->fd, &fp)) != 0)
  846            return (error);
  847    /* We simply copy the data and send it right to ioctl */
  848    copyin((caddr_t)args->arg, &d_pio, sizeof(d_pio));
  849    error = fo_ioctl(fp, cmd, (caddr_t)&d_pio, td->td_ucred, td);
  850    fdrop(fp, td);
  851    return error;
  852 }
  853 #endif /* TDFX_LINUX */
  854 
  855 
  856 /* This is the device driver struct. This is sent to the driver subsystem to
  857  * register the method structure and the info strcut space for this particular
  858  * instance of the driver.
  859  */
  860 static driver_t tdfx_driver = {
  861         "tdfx", 
  862         tdfx_methods,
  863         sizeof(struct tdfx_softc),
  864 };
  865 
  866 /* Tell Mr. Kernel about us! */
  867 DRIVER_MODULE(tdfx, pci, tdfx_driver, tdfx_devclass, 0, 0);

Cache object: 6e5e7ce3faa367c55eb8d9da42f75d2f


[ 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.