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