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