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