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

Cache object: c20bc546e8f78fd7ec5e76e959d7338e


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