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