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