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