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

Cache object: 3ef5bac9612c3faad53675118eec24ba


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