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