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