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