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

Cache object: 3ef6e706070e5fcca4c0cfd00cc2a175


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