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