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

Cache object: dd8f286ee0fee91b0a438170ea64b285


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