tdfx_pci.c revision 61985
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 61985 2000-06-23 04:27:33Z 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_linux.h>
82#include <dev/tdfx/tdfx_io.h>
83#include <dev/tdfx/tdfx_vars.h>
84#include <dev/tdfx/tdfx_pci.h>
85
86#include "opt_tdfx.h"
87
88static devclass_t tdfx_devclass;
89
90
91static int tdfx_count = 0;
92
93
94/* Set up the boot probe/attach routines */
95static device_method_t tdfx_methods[] = {
96	DEVMETHOD(device_probe,		tdfx_probe),
97	DEVMETHOD(device_attach,	tdfx_attach),
98	DEVMETHOD(device_detach,	tdfx_detach),
99	DEVMETHOD(device_shutdown,	tdfx_shutdown),
100	{ 0, 0 }
101};
102
103MALLOC_DEFINE(M_TDFX,"TDFX Driver","3DFX Graphics[/2D]/3D Accelerator(s)");
104
105#ifdef TDFX_LINUX
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	make_dev(&tdfx_cdev, dev->unit, 0, 0, 02660, "3dfx%x", dev->unit);
240
241	return 0;
242}
243
244static int
245tdfx_detach(device_t dev) {
246	struct tdfx_softc* tdfx_info;
247	int retval;
248	tdfx_info = device_get_softc(dev);
249
250	/* Delete allocated resource, of course */
251	bus_release_resource(dev, SYS_RES_MEMORY, PCI_MAP_REG_START,
252			tdfx_info->memrange);
253
254	/* Though it is safe to leave the WRCOMB support since the
255		mem driver checks for it, we should remove it in order
256		to free an MTRR for another device */
257	retval = tdfx_clrmtrr(dev);
258#ifdef DEBUG
259	if(retval != 0)
260		printf("tdfx: For some reason, I couldn't clear the mtrr\n");
261#endif
262	return(0);
263}
264
265static int
266tdfx_shutdown(device_t dev) {
267#ifdef DEBUG
268	device_printf(dev, "tdfx: Device Shutdown\n");
269#endif
270	return 0;
271}
272
273static int
274tdfx_clrmtrr(device_t dev) {
275	/* This function removes the MTRR set by the attach call, so it can be used
276	 * in the future by other drivers.
277	 */
278	int retval, act;
279	struct tdfx_softc *tdfx_info = device_get_softc(dev);
280
281	act = MEMRANGE_SET_REMOVE;
282	retval = mem_range_attr_set(&tdfx_info->mrdesc, &act);
283	return retval;
284}
285
286static int
287tdfx_setmtrr(device_t dev) {
288	/*
289	 * This is the MTRR setting function for the 3dfx card. It is called from
290	 * tdfx_attach. If we can't set the MTRR properly, it's not the end of the
291	 * world. We can still continue, just with slightly (very slightly) degraded
292	 * performance.
293	 */
294	int retval = 0, act;
295	struct tdfx_softc *tdfx_info = device_get_softc(dev);
296	/* The memory descriptor is described as the top 15 bits of the real
297		address */
298	tdfx_info->mrdesc.mr_base = pci_read_config(dev, 0x10, 4) & 0xfffe0000;
299
300	/* The older Voodoo cards have a shorter memrange than the newer ones */
301	if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1) || (pci_get_devid(dev) ==
302			PCI_DEVICE_3DFX_VOODOO2))
303		tdfx_info->mrdesc.mr_len = 0x400000;
304	else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) ||
305			(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE))
306		tdfx_info->mrdesc.mr_len = 0x1000000;
307
308	else return 0;
309	/*
310    *	The Alliance Pro Motion AT3D was not mentioned in the linux
311	 * driver as far as MTRR support goes, so I just won't put the
312	 * code in here for it. This is where it should go, though.
313	 */
314
315	/* Firstly, try to set write combining */
316	tdfx_info->mrdesc.mr_flags = MDF_WRITECOMBINE;
317	bcopy("tdfx", &tdfx_info->mrdesc.mr_owner, 4);
318	act = MEMRANGE_SET_UPDATE;
319	retval = mem_range_attr_set(&tdfx_info->mrdesc, &act);
320
321	if(retval == 0) {
322#ifdef DEBUG
323		device_printf(dev, "MTRR Set Correctly for tdfx\n");
324#endif
325	} else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO2) ||
326		(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1)) {
327		/* if, for some reason we can't set the WRCOMB range with the V1/V2, we
328		 * can still possibly use the UNCACHEABLE region for it instead, and help
329		 * out in a small way */
330		tdfx_info->mrdesc.mr_flags = MDF_UNCACHEABLE;
331		/* This length of 1000h was taken from the linux device driver... */
332		tdfx_info->mrdesc.mr_len = 0x1000;
333
334		/*
335		 * If, for some reason, we can't set the MTRR (N/A?) we may still continue
336		 */
337#ifdef DEBUG
338		if(retval == 0) {
339			device_printf(dev, "MTRR Set Type Uncacheable
340					%x\n", (u_int32_t)tdfx_info->mrdesc.mr_base);
341		} else {
342			device_printf(dev, "Couldn't Set MTRR\n");
343		}
344#endif
345	}
346#ifdef DEBUG
347	else {
348		device_printf(dev, "Couldn't Set MTRR\n");
349		return 0;
350	}
351#endif
352	return 0;
353}
354
355static int
356tdfx_open(dev_t dev, int flags, int fmt, struct proc *p)
357{
358	/*
359	 *	The open cdev method handles open(2) calls to /dev/3dfx[n]
360	 * We can pretty much allow any opening of the device.
361	 */
362	struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass,
363			UNIT(minor(dev)));
364	if(tdfx_info->busy != 0) return EBUSY;
365#ifdef	DEBUG
366	printf("3dfx: Opened by #%d\n", p->p_pid);
367#endif
368	/* Set the driver as busy */
369	tdfx_info->busy++;
370	return 0;
371}
372
373static int
374tdfx_close(dev_t dev, int fflag, int devtype, struct proc* p)
375{
376	/*
377	 *	The close cdev method handles close(2) calls to /dev/3dfx[n]
378	 * We'll always want to close the device when it's called.
379	 */
380	struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass,
381		UNIT(minor(dev)));
382	if(tdfx_info->busy == 0) return EBADF;
383	tdfx_info->busy = 0;
384#ifdef	DEBUG
385	printf("Closed by #%d\n", p->p_pid);
386#endif
387	return 0;
388}
389
390static int
391tdfx_mmap(dev_t dev, vm_offset_t offset, int nprot)
392{
393	/*
394	 * mmap(2) is called by a user process to request that an area of memory
395	 * associated with this device be mapped for the process to work with. Nprot
396	 * holds the protections requested, PROT_READ, PROT_WRITE, or both.
397	 */
398	struct tdfx_softc* tdfx_info;
399
400	/* Get the configuration for our card XXX*/
401	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
402			UNIT(minor(dev)));
403
404	/* If, for some reason, its not configured, we bail out */
405	if(tdfx_info == NULL) {
406#ifdef	DEBUG
407	   printf("tdfx: tdfx_info (softc) is NULL\n");
408#endif
409	   return -1;
410	}
411
412	/* We must stay within the bound of our address space */
413	if((offset & 0xff000000) == tdfx_info->addr0)
414		offset &= 0xffffff;
415	if((offset >= 0x1000000) || (offset < 0)) {
416#ifdef  DEBUG
417	   printf("tdfx: offset %x out of range\n", offset);
418#endif
419	   return -1;
420	}
421
422	/* atop -> address to page
423	 * rman_get_start, get the (struct resource*)->r_start member,
424	 * the mapping base address.
425	 */
426	return atop(rman_get_start(tdfx_info->memrange) + offset);
427}
428
429static int
430tdfx_query_boards(void) {
431	/*
432    *	This returns the number of installed tdfx cards, we have been keeping
433	 * count, look at tdfx_attach
434	 */
435	return tdfx_count;
436}
437
438static int
439tdfx_query_fetch(u_int cmd, struct tdfx_pio_data *piod)
440{
441	/* XXX Comment this later, after careful inspection and spring cleaning :) */
442	/* Various return values 8bit-32bit */
443	u_int8_t  ret_byte;
444	u_int16_t ret_word;
445	u_int32_t ret_dword;
446	struct tdfx_softc* tdfx_info = NULL;
447
448	/* This one depend on the tdfx_* structs being properly initialized */
449
450	/*piod->device &= 0xf;*/
451	if((piod == NULL) ||(tdfx_count <= piod->device) ||
452			(piod->device < 0)) {
453#ifdef DEBUG
454		printf("tdfx: Bad device or internal struct in tdfx_query_fetch\n");
455#endif
456		return -EINVAL;
457	}
458
459	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
460			piod->device);
461
462	if(tdfx_info == NULL) return -ENXIO;
463
464	/* We must restrict the size reads from the port, since to high or low of a
465	 * size witll result in wrong data being passed, and that's bad */
466	/* A few of these were pulled during the attach phase */
467	switch(piod->port) {
468		case PCI_VENDOR_ID_FREEBSD:
469			if(piod->size != 2) return -EINVAL;
470			copyout(&tdfx_info->vendor, piod->value, piod->size);
471			return 0;
472		case PCI_DEVICE_ID_FREEBSD:
473			if(piod->size != 2) return -EINVAL;
474			copyout(&tdfx_info->type, piod->value, piod->size);
475			return 0;
476		case PCI_BASE_ADDRESS_0_FREEBSD:
477			if(piod->size != 4) return -EINVAL;
478			copyout(&tdfx_info->addr0, piod->value, piod->size);
479			return 0;
480		case SST1_PCI_SPECIAL1_FREEBSD:
481			if(piod->size != 4) return -EINVAL;
482			break;
483		case PCI_REVISION_ID_FREEBSD:
484			if(piod->size != 1) return -EINVAL;
485			break;
486		case SST1_PCI_SPECIAL4_FREEBSD:
487			if(piod->size != 4) return -EINVAL;
488			break;
489		default:
490			return -EINVAL;
491	}
492
493
494	/* Read the value and return */
495	switch(piod->size) {
496		case 1:
497			ret_byte = pci_read_config(tdfx_info[piod->device].dev,
498					piod->port, 1);
499			copyout(&ret_byte, piod->value, 1);
500			break;
501		case 2:
502			ret_word = pci_read_config(tdfx_info[piod->device].dev,
503					piod->port, 2);
504			copyout(&ret_word, piod->value, 2);
505			break;
506		case 4:
507			ret_dword = pci_read_config(tdfx_info[piod->device].dev,
508					piod->port, 4);
509			copyout(&ret_dword, piod->value, 4);
510			break;
511		default:
512			return -EINVAL;
513	}
514	return 0;
515}
516
517static int
518tdfx_query_update(u_int cmd, struct tdfx_pio_data *piod)
519{
520	/* XXX Comment this later, after careful inspection and spring cleaning :) */
521	/* Return vals */
522	u_int8_t  ret_byte;
523	u_int16_t ret_word;
524	u_int32_t ret_dword;
525
526	/* Port vals, mask */
527	u_int32_t retval, preval, mask;
528	struct tdfx_softc* tdfx_info = NULL;
529
530
531	if((piod == NULL) || (piod->device >= (tdfx_count &
532					0xf))) {
533#ifdef DEBUG
534		printf("tdfx: Bad struct or device in tdfx_query_update\n");
535#endif
536		return -EINVAL;
537	}
538
539	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
540			piod->device);
541	if(tdfx_info == NULL) return -ENXIO;
542	/* Code below this line in the fuction was taken from the
543	 * Linux driver and converted for freebsd. */
544
545	/* Check the size for all the ports, to make sure stuff doesn't get messed up
546	 * by poorly written clients */
547
548	switch(piod->port) {
549		case PCI_COMMAND_FREEBSD:
550			if(piod->size != 2) return -EINVAL;
551			break;
552		case SST1_PCI_SPECIAL1_FREEBSD:
553			if(piod->size != 4) return -EINVAL;
554			break;
555		case SST1_PCI_SPECIAL2_FREEBSD:
556			if(piod->size != 4) return -EINVAL;
557			break;
558		case SST1_PCI_SPECIAL3_FREEBSD:
559			if(piod->size != 4) return -EINVAL;
560			break;
561		case SST1_PCI_SPECIAL4_FREEBSD:
562			if(piod->size != 4) return -EINVAL;
563			break;
564		default:
565			return -EINVAL;
566	}
567	/* Read the current value */
568	retval = pci_read_config(tdfx_info->dev, piod->port & ~3, 4);
569
570	/* These set up a mask to use, since apparently they wanted to write 4 bytes
571	 * at once to the ports */
572	switch (piod->size) {
573		case 1:
574			copyin(piod->value, &ret_byte, 1);
575			preval = ret_byte << (8 * (piod->port & 0x3));
576			mask = 0xff << (8 * (piod->port & 0x3));
577			break;
578		case 2:
579			copyin(piod->value, &ret_word, 2);
580			preval = ret_word << (8 * (piod->port & 0x3));
581			mask = 0xffff << (8 * (piod->port & 0x3));
582			break;
583		case 4:
584			copyin(piod->value, &ret_dword, 4);
585			preval = ret_dword;
586			mask = ~0;
587			break;
588		default:
589			return -EINVAL;
590	}
591	/* Finally, combine the values and write it to the port */
592	retval = (retval & ~mask) | preval;
593	pci_write_config(tdfx_info->dev, piod->port & ~3, retval, 4);
594
595	return 0;
596}
597
598static int
599tdfx_do_pio_rd(struct tdfx_pio_data *piod)
600{
601	/* Return val */
602	u_int8_t  ret_byte;
603
604	/* Restricts the access of ports other than those we use */
605	if((piod->port != VGA_INPUT_STATUS_1C) || (piod->port != SC_INDEX) ||
606		(piod->port != SC_DATA) || (piod->port != VGA_MISC_OUTPUT_READ))
607		return -EPERM;
608
609	/* All VGA STATUS REGS are byte registers, size should never be > 1 */
610	if(piod->size != 1) {
611		return -EINVAL;
612	}
613
614	/* Write the data to the intended port */
615	ret_byte = inb(piod->port);
616	copyout(&ret_byte, piod->value, sizeof(u_int8_t));
617	return 0;
618}
619
620static int
621tdfx_do_pio_wt(struct tdfx_pio_data *piod)
622{
623	/* return val */
624	u_int8_t  ret_byte;
625
626	/* Replace old switch w/ massive if(...) */
627	/* Restricts the access of ports other than those we use */
628	if((piod->port != SC_INDEX) && (piod->port != SC_DATA) &&
629		(piod->port != VGA_MISC_OUTPUT_READ)) /* Can't write VGA_ST_1C */
630		return -EPERM;
631
632	/* All VGA STATUS REGS are byte registers, size should never be > 1 */
633	if(piod->size != 1) {
634		return -EINVAL;
635	}
636
637	/* Write the data to the intended port */
638	copyin(piod->value, &ret_byte, sizeof(u_int8_t));
639	outb(piod->port, ret_byte);
640	return 0;
641}
642
643static int
644tdfx_do_query(u_int cmd, struct tdfx_pio_data *piod)
645{
646	/* There are three sub-commands to the query 0x33 */
647	switch(_IOC_NR(cmd)) {
648		case 2:
649			return tdfx_query_boards();
650			break;
651		case 3:
652			return tdfx_query_fetch(cmd, piod);
653			break;
654		case 4:
655			return tdfx_query_update(cmd, piod);
656			break;
657		default:
658			/* In case we are thrown a bogus sub-command! */
659#ifdef DEBUG
660			printf("Bad Sub-cmd: 0x%x\n", _IOC_NR(cmd));
661#endif
662			return -EINVAL;
663	};
664}
665
666static int
667tdfx_do_pio(u_int cmd, struct tdfx_pio_data *piod)
668{
669	/* Two types of PIO, INPUT and OUTPUT, as the name suggests */
670	switch(_IOC_DIR(cmd)) {
671		case IOCV_OUT:
672			return tdfx_do_pio_rd(piod);
673			break;
674		case IOCV_IN:
675			return tdfx_do_pio_wt(piod);
676			break;
677		default:
678			return -EINVAL;
679	};
680}
681
682/* Calls to ioctl(2) eventually end up here. Unhandled ioctls return an ENXIO,
683 * normally, you would read in the data pointed to by data, then write your
684 * output to it. The ioctl *should* normally return zero if everything is
685 * alright, but 3dfx didn't make it that way...
686 *
687 * For all of the ioctl code, in the event of a real error,
688 * we return -Exxxx rather than simply Exxxx. The reason for this
689 * is that the ioctls actually RET information back to the program
690 * sometimes, rather than filling it in the passed structure. We
691 * want to distinguish errors from useful data, and maintain compatibility.
692 *
693 * There is this portion of the proc struct called p_retval[], we can store a
694 * return value in p->p_retval[0] and place the return value if it is positive
695 * in there, then we can return 0 (good). If the return value is negative, we
696 * can return -retval and the error should be properly handled.
697 */
698static int
699tdfx_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc * p)
700{
701	int retval = 0;
702	struct tdfx_pio_data *piod = (struct tdfx_pio_data*)data;
703#ifdef	DEBUG
704	printf("IOCTL'd by #%d, cmd: 0x%x, data: 0x%x\n", p->p_pid, (u_int32_t)cmd,
705			(unsigned int)piod);
706#endif
707	switch(_IOC_TYPE(cmd)) {
708		/* Return the real error if negative, or simply stick the valid return
709		 * in p->p_retval */
710	case 0x33:
711			/* The '3'(0x33) type IOCTL is for querying the installed cards */
712			if((retval = tdfx_do_query(cmd, piod)) > 0) p->p_retval[0] = retval;
713			else return -retval;
714			break;
715		case 0:
716			/* The 0 type IOCTL is for programmed I/O methods */
717			if((tdfx_do_pio(cmd, piod)) > 0) p->p_retval[0] = retval;
718			else return -retval;
719			break;
720		default:
721			/* Technically, we won't reach this from linux emu, but when glide
722			 * finally gets ported, watch out! */
723#ifdef DEBUG
724			printf("Bad IOCTL from #%d\n", p->p_pid);
725#endif
726			return ENXIO;
727	}
728
729	return 0;
730}
731
732#ifdef TDFX_LINUX
733/*
734 * Linux emulation IOCTL for /dev/tdfx
735 */
736static int
737linux_ioctl_tdfx(struct proc* p, struct linux_ioctl_args* args)
738{
739   int error = 0;
740   u_long cmd = args->cmd & 0xffff;
741
742   /* The structure passed to ioctl has two shorts, one int
743      and one void*. */
744   char d_pio[2*sizeof(short) + sizeof(int) + sizeof(void*)];
745
746   struct file *fp = p->p_fd->fd_ofiles[args->fd];
747
748   /* We simply copy the data and send it right to ioctl */
749   copyin((caddr_t)args->arg, &d_pio, sizeof(d_pio));
750   error = fo_ioctl(fp, cmd, (caddr_t)&d_pio, p);
751   return error;
752}
753#endif /* TDFX_LINUX */
754
755
756/* This is the device driver struct. This is sent to the driver subsystem to
757 * register the method structure and the info strcut space for this particular
758 * instance of the driver.
759 */
760static driver_t tdfx_driver = {
761	"tdfx",
762	tdfx_methods,
763	sizeof(struct tdfx_softc),
764};
765
766/* Tell Mr. Kernel about us! */
767DRIVER_MODULE(tdfx, pci, tdfx_driver, tdfx_devclass, 0, 0);
768
769
770#endif	/* NPCI */
771