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