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