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