tdfx_pci.c revision 61989
161931Scokane/*
261931Scokane * Copyright (c) 2000 by Coleman Kane <cokane@FreeBSD.org>
361931Scokane * All rights reserved.
461931Scokane *
561931Scokane * Redistribution and use in source and binary forms, with or without
661931Scokane * modification, are permitted provided that the following conditions
761931Scokane * are met:
861931Scokane * 1. Redistributions of source code must retain the above copyright
961931Scokane *    notice, this list of conditions and the following disclaimer.
1061931Scokane * 2. Redistributions in binary form must reproduce the above copyright
1161931Scokane *    notice, this list of conditions and the following disclaimer in the
1261931Scokane *    documentation and/or other materials provided with the distribution.
1361931Scokane * 3. All advertising materials mentioning features or use of this software
1461931Scokane *    must display the following acknowledgement:
1561931Scokane *      This product includes software developed by Gardner Buchanan.
1661931Scokane * 4. The name of Gardner Buchanan may not be used to endorse or promote
1761931Scokane *    products derived from this software without specific prior written
1861931Scokane *    permission.
1961931Scokane *
2061931Scokane * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2161931Scokane * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2261931Scokane * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2361931Scokane * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2461931Scokane * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2561931Scokane * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2661931Scokane * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2761931Scokane * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2861931Scokane * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2961931Scokane * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3061931Scokane *
3161931Scokane *   $FreeBSD: head/sys/dev/tdfx/tdfx_pci.c 61989 2000-06-23 05:54:01Z cokane $
3261931Scokane */
3361931Scokane
3461911Scokane/* 3dfx driver for FreeBSD 4.x - Finished 11 May 2000, 12:25AM ET
3561911Scokane *
3661931Scokane * Copyright (C) 2000, by Coleman Kane <cokane@FreeBSD.org>,
3761911Scokane * based upon the 3dfx driver written for linux, by Daryll Straus, Jon Taylor,
3861911Scokane * and Jens Axboe, located at http://linux.3dfx.com.
3961911Scokane */
4061911Scokane
4161911Scokane/*
4261911Scokane * put this here, so as to bail out immediately if we have no PCI BUS installed
4361911Scokane */
4461911Scokane#include	"pci.h"
4561911Scokane#if NPCI > 0
4661911Scokane
4761911Scokane#include <sys/param.h>
4861911Scokane
4961911Scokane#include <sys/bus_private.h>
5061911Scokane#include <sys/bus.h>
5161911Scokane#include <sys/cdefs.h>
5261911Scokane#include <sys/conf.h>
5361911Scokane#include <sys/fcntl.h>
5461911Scokane#include <sys/file.h>
5561911Scokane#include <sys/filedesc.h>
5661911Scokane#include <sys/filio.h>
5761911Scokane#include <sys/ioccom.h>
5861911Scokane#include <sys/kernel.h>
5961911Scokane#include	<sys/malloc.h>
6061911Scokane#include <sys/mman.h>
6161911Scokane#include <sys/signalvar.h>
6261911Scokane#include <sys/systm.h>
6361911Scokane#include <sys/uio.h>
6461911Scokane
6561911Scokane#include <pci/pcivar.h>
6661911Scokane#include <pci/pcireg.h>
6761911Scokane
6861911Scokane#include <vm/vm.h>
6961911Scokane#include <vm/vm_kern.h>
7061911Scokane#include <vm/pmap.h>
7161911Scokane#include <vm/vm_extern.h>
7261911Scokane
7361911Scokane/* rman.h depends on machine/bus.h */
7461911Scokane#include <machine/resource.h>
7561911Scokane#include <machine/bus.h>
7661911Scokane#include <sys/rman.h>
7761931Scokane#ifdef TDFX_LINUX
7861931Scokane#include <dev/tdfx/tdfx_linux.h>
7961931Scokane#endif
8061911Scokane
8161911Scokane#include <dev/tdfx/tdfx_io.h>
8261911Scokane#include <dev/tdfx/tdfx_vars.h>
8361911Scokane#include <dev/tdfx/tdfx_pci.h>
8461911Scokane
8561911Scokane#include "opt_tdfx.h"
8661911Scokane
8761911Scokanestatic devclass_t tdfx_devclass;
8861911Scokane
8961911Scokane
9061911Scokanestatic int tdfx_count = 0;
9161911Scokane
9261911Scokane
9361911Scokane/* Set up the boot probe/attach routines */
9461911Scokanestatic device_method_t tdfx_methods[] = {
9561911Scokane	DEVMETHOD(device_probe,		tdfx_probe),
9661911Scokane	DEVMETHOD(device_attach,	tdfx_attach),
9761911Scokane	DEVMETHOD(device_detach,	tdfx_detach),
9861911Scokane	DEVMETHOD(device_shutdown,	tdfx_shutdown),
9961911Scokane	{ 0, 0 }
10061911Scokane};
10161911Scokane
10261911ScokaneMALLOC_DEFINE(M_TDFX,"TDFX Driver","3DFX Graphics[/2D]/3D Accelerator(s)");
10361911Scokane
10461931Scokane#ifdef TDFX_LINUX
10561989ScokaneMODULE_DEPEND(tdfx, linux, 1, 1, 1);
10661931ScokaneLINUX_IOCTL_SET(tdfx, LINUX_IOCTL_TDFX_MIN, LINUX_IOCTL_TDFX_MAX);
10761931Scokane#endif
10861931Scokane
10961911Scokane/* Char. Dev. file operations structure */
11061911Scokanestatic struct cdevsw tdfx_cdev = {
11161911Scokane	tdfx_open,		/* open */
11261911Scokane	tdfx_close,		/* close */
11361911Scokane	noread,			/* read */
11461911Scokane	nowrite,			/* write */
11561911Scokane	tdfx_ioctl,		/* ioctl */
11661911Scokane	nopoll,			/* poll */
11761911Scokane	tdfx_mmap,		/* mmap */
11861911Scokane	nostrategy,		/* strategy */
11961911Scokane	"tdfx",			/* dev name */
12061911Scokane	CDEV_MAJOR, 	/* char major */
12161911Scokane	nodump,			/* dump */
12261911Scokane	nopsize,			/* size */
12361911Scokane	0,					/* flags (no set flags) */
12461911Scokane	-1 				/* bmaj (no block dev) */
12561911Scokane};
12661911Scokane
12761911Scokanestatic int
12861911Scokanetdfx_probe(device_t dev)
12961911Scokane{
13061911Scokane	/*
13161911Scokane	 * probe routine called on kernel boot to register supported devices. We get
13261911Scokane	 * a device structure to work with, and we can test the VENDOR/DEVICE IDs to
13361911Scokane	 * see if this PCI device is one that we support. Return 0 if yes, ENXIO if
13461911Scokane	 * not.
13561911Scokane	 */
13661911Scokane	switch(pci_get_devid(dev)) {
13761911Scokane	case PCI_DEVICE_ALLIANCE_AT3D:
13861911Scokane		device_set_desc(dev, "ProMotion At3D 3D Accelerator");
13961911Scokane		return 0;
14061911Scokane	case PCI_DEVICE_3DFX_VOODOO2:
14161911Scokane		device_set_desc(dev, "3DFX Voodoo II 3D Accelerator");
14261911Scokane		return 0;
14361911Scokane	case PCI_DEVICE_3DFX_BANSHEE:
14461911Scokane		device_set_desc(dev, "3DFX Voodoo Banshee 2D/3D Graphics Accelerator");
14561911Scokane		return 0;
14661911Scokane	case PCI_DEVICE_3DFX_VOODOO3:
14761911Scokane		device_set_desc(dev, "3DFX Voodoo3 2D/3D Graphics Accelerator");
14861911Scokane		return 0;
14961911Scokane	case PCI_DEVICE_3DFX_VOODOO1:
15061911Scokane		device_set_desc(dev, "3DFX Voodoo Graphics 3D Accelerator");
15161911Scokane		return 0;;
15261911Scokane	};
15361911Scokane
15461911Scokane	return ENXIO;
15561911Scokane}
15661911Scokane
15761911Scokanestatic int
15861911Scokanetdfx_attach(device_t dev) {
15961911Scokane	/*
16061911Scokane	 * The attach routine is called after the probe routine successfully says it
16161911Scokane	 * supports a given card. We now proceed to initialize this card for use with
16261911Scokane	 * the system. I want to map the device memory for userland allocation and
16361911Scokane	 * fill an information structure with information on this card. I'd also like
16461911Scokane	 * to set Write Combining with the MTRR code so that we can hopefully speed
16561911Scokane	 * up memory writes. The last thing is to register the character device
16661911Scokane	 * interface to the card, so we can open it from /dev/3dfxN, where N is a
16761911Scokane	 * small, whole number.
16861911Scokane	 */
16961911Scokane
17061911Scokane	struct tdfx_softc *tdfx_info;
17161911Scokane	u_long	val;
17261911Scokane	/* rid value tells bus_alloc_resource where to find the addresses of ports or
17361911Scokane	 * of memory ranges in the PCI config space*/
17461911Scokane	int rid = PCIR_MAPS;
17561911Scokane
17661911Scokane	/* Increment the card counter (for the ioctl code) */
17761911Scokane	tdfx_count++;
17861911Scokane
17961911Scokane 	/* Enable MemMap on Voodoo */
18061911Scokane	val = pci_read_config(dev, PCIR_COMMAND, 2);
18161911Scokane	val |= (PCIM_CMD_MEMEN);
18261911Scokane	pci_write_config(dev, PCIR_COMMAND, val, 2);
18361911Scokane	val = pci_read_config(dev, PCIR_COMMAND, 2);
18461911Scokane
18561911Scokane	/* Fill the soft config struct with info about this device*/
18661911Scokane	tdfx_info = device_get_softc(dev);
18761911Scokane	tdfx_info->dev = dev;
18861911Scokane	tdfx_info->vendor = pci_get_vendor(dev);
18961911Scokane	tdfx_info->type = pci_get_devid(dev) >> 16;
19061911Scokane	tdfx_info->bus = pci_get_bus(dev);
19161911Scokane	tdfx_info->dv = pci_get_slot(dev);
19261911Scokane	tdfx_info->curFile = NULL;
19361911Scokane
19461911Scokane	/*
19561911Scokane	 *	Get the Memory Location from the PCI Config, mask out lower word, since
19661911Scokane	 * the config space register is only one word long (this is nicer than a
19761911Scokane	 * bitshift).
19861911Scokane	 */
19961911Scokane	tdfx_info->addr0 = (pci_read_config(dev, 0x10, 4) & 0xffff0000);
20061931Scokane#ifdef DEBUG
20161911Scokane	device_printf(dev, "Base0 @ 0x%x\n", tdfx_info->addr0);
20261911Scokane#endif
20361911Scokane
20461911Scokane	/* Notify the VM that we will be mapping some memory later */
20561911Scokane	tdfx_info->memrange = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, ~0, 1,
20661967Scokane			RF_ACTIVE | RF_SHAREABLE);
20761911Scokane	if(tdfx_info->memrange == NULL) {
20861931Scokane#ifdef DEBUG
20961911Scokane		device_printf(dev, "Error mapping mem, won't be able to use mmap()\n");
21061911Scokane#endif
21161911Scokane		tdfx_info->memrid = 0;
21261911Scokane	}
21361911Scokane	else {
21461911Scokane		tdfx_info->memrid = rid;
21561931Scokane#ifdef DEBUG
21661911Scokane		device_printf(dev, "Mapped to: 0x%x\n",
21761911Scokane				(unsigned int)rman_get_start(tdfx_info->memrange));
21861911Scokane#endif
21961911Scokane	}
22061911Scokane
22161911Scokane	/*
22261911Scokane	 *	Set Writecombining, or at least Uncacheable for the memory region, if we
22361911Scokane	 * are able to
22461911Scokane	 */
22561911Scokane
22661911Scokane	if(tdfx_setmtrr(dev) != 0) {
22761931Scokane#ifdef DEBUG
22861911Scokane		device_printf(dev, "Some weird error setting MTRRs");
22961911Scokane#endif
23061911Scokane		return -1;
23161911Scokane	}
23261911Scokane
23361911Scokane	/*
23461911Scokane	 * make_dev registers the cdev to access the 3dfx card from /dev
23561911Scokane	 *	use hex here for the dev num, simply to provide better support if > 10
23661911Scokane	 * voodoo cards, for the mad. The user must set the link, or use MAKEDEV.
23761911Scokane	 * Why would we want that many voodoo cards anyhow?
23861911Scokane	 */
23961989Scokane	tdfx_info->devt = make_dev(&tdfx_cdev, dev->unit, 0, 0, 02660,
24061989Scokane		"3dfx%x", dev->unit);
24161911Scokane
24261911Scokane	return 0;
24361911Scokane}
24461911Scokane
24561911Scokanestatic int
24661911Scokanetdfx_detach(device_t dev) {
24761911Scokane	struct tdfx_softc* tdfx_info;
24861911Scokane	int retval;
24961911Scokane	tdfx_info = device_get_softc(dev);
25061911Scokane
25161911Scokane	/* Delete allocated resource, of course */
25261911Scokane	bus_release_resource(dev, SYS_RES_MEMORY, PCI_MAP_REG_START,
25361911Scokane			tdfx_info->memrange);
25461911Scokane
25561911Scokane	/* Though it is safe to leave the WRCOMB support since the
25661911Scokane		mem driver checks for it, we should remove it in order
25761911Scokane		to free an MTRR for another device */
25861911Scokane	retval = tdfx_clrmtrr(dev);
25961931Scokane#ifdef DEBUG
26061911Scokane	if(retval != 0)
26161911Scokane		printf("tdfx: For some reason, I couldn't clear the mtrr\n");
26261911Scokane#endif
26361989Scokane	/* Remove device entry when it can no longer be accessed */
26461989Scokane   destroy_dev(tdfx_info->devt);
26561911Scokane	return(0);
26661911Scokane}
26761911Scokane
26861911Scokanestatic int
26961911Scokanetdfx_shutdown(device_t dev) {
27061931Scokane#ifdef DEBUG
27161911Scokane	device_printf(dev, "tdfx: Device Shutdown\n");
27261911Scokane#endif
27361911Scokane	return 0;
27461911Scokane}
27561911Scokane
27661911Scokanestatic int
27761911Scokanetdfx_clrmtrr(device_t dev) {
27861911Scokane	/* This function removes the MTRR set by the attach call, so it can be used
27961911Scokane	 * in the future by other drivers.
28061911Scokane	 */
28161911Scokane	int retval, act;
28261911Scokane	struct tdfx_softc *tdfx_info = device_get_softc(dev);
28361911Scokane
28461911Scokane	act = MEMRANGE_SET_REMOVE;
28561911Scokane	retval = mem_range_attr_set(&tdfx_info->mrdesc, &act);
28661911Scokane	return retval;
28761911Scokane}
28861911Scokane
28961911Scokanestatic int
29061911Scokanetdfx_setmtrr(device_t dev) {
29161911Scokane	/*
29261911Scokane	 * This is the MTRR setting function for the 3dfx card. It is called from
29361911Scokane	 * tdfx_attach. If we can't set the MTRR properly, it's not the end of the
29461911Scokane	 * world. We can still continue, just with slightly (very slightly) degraded
29561911Scokane	 * performance.
29661911Scokane	 */
29761911Scokane	int retval = 0, act;
29861911Scokane	struct tdfx_softc *tdfx_info = device_get_softc(dev);
29961911Scokane	/* The memory descriptor is described as the top 15 bits of the real
30061911Scokane		address */
30161911Scokane	tdfx_info->mrdesc.mr_base = pci_read_config(dev, 0x10, 4) & 0xfffe0000;
30261911Scokane
30361911Scokane	/* The older Voodoo cards have a shorter memrange than the newer ones */
30461911Scokane	if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1) || (pci_get_devid(dev) ==
30561911Scokane			PCI_DEVICE_3DFX_VOODOO2))
30661911Scokane		tdfx_info->mrdesc.mr_len = 0x400000;
30761911Scokane	else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) ||
30861911Scokane			(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE))
30961911Scokane		tdfx_info->mrdesc.mr_len = 0x1000000;
31061911Scokane
31161911Scokane	else return 0;
31261911Scokane	/*
31361911Scokane    *	The Alliance Pro Motion AT3D was not mentioned in the linux
31461911Scokane	 * driver as far as MTRR support goes, so I just won't put the
31561911Scokane	 * code in here for it. This is where it should go, though.
31661911Scokane	 */
31761911Scokane
31861911Scokane	/* Firstly, try to set write combining */
31961911Scokane	tdfx_info->mrdesc.mr_flags = MDF_WRITECOMBINE;
32061911Scokane	bcopy("tdfx", &tdfx_info->mrdesc.mr_owner, 4);
32161911Scokane	act = MEMRANGE_SET_UPDATE;
32261911Scokane	retval = mem_range_attr_set(&tdfx_info->mrdesc, &act);
32361911Scokane
32461911Scokane	if(retval == 0) {
32561931Scokane#ifdef DEBUG
32661911Scokane		device_printf(dev, "MTRR Set Correctly for tdfx\n");
32761911Scokane#endif
32861911Scokane	} else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO2) ||
32961911Scokane		(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1)) {
33061911Scokane		/* if, for some reason we can't set the WRCOMB range with the V1/V2, we
33161911Scokane		 * can still possibly use the UNCACHEABLE region for it instead, and help
33261911Scokane		 * out in a small way */
33361911Scokane		tdfx_info->mrdesc.mr_flags = MDF_UNCACHEABLE;
33461911Scokane		/* This length of 1000h was taken from the linux device driver... */
33561911Scokane		tdfx_info->mrdesc.mr_len = 0x1000;
33661911Scokane
33761911Scokane		/*
33861911Scokane		 * If, for some reason, we can't set the MTRR (N/A?) we may still continue
33961911Scokane		 */
34061931Scokane#ifdef DEBUG
34161911Scokane		if(retval == 0) {
34261911Scokane			device_printf(dev, "MTRR Set Type Uncacheable
34361911Scokane					%x\n", (u_int32_t)tdfx_info->mrdesc.mr_base);
34461911Scokane		} else {
34561911Scokane			device_printf(dev, "Couldn't Set MTRR\n");
34661911Scokane		}
34761911Scokane#endif
34861911Scokane	}
34961931Scokane#ifdef DEBUG
35061911Scokane	else {
35161911Scokane		device_printf(dev, "Couldn't Set MTRR\n");
35261911Scokane		return 0;
35361911Scokane	}
35461911Scokane#endif
35561911Scokane	return 0;
35661911Scokane}
35761911Scokane
35861911Scokanestatic int
35961911Scokanetdfx_open(dev_t dev, int flags, int fmt, struct proc *p)
36061911Scokane{
36161911Scokane	/*
36261911Scokane	 *	The open cdev method handles open(2) calls to /dev/3dfx[n]
36361911Scokane	 * We can pretty much allow any opening of the device.
36461911Scokane	 */
36561911Scokane	struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass,
36661911Scokane			UNIT(minor(dev)));
36761911Scokane	if(tdfx_info->busy != 0) return EBUSY;
36861931Scokane#ifdef	DEBUG
36961911Scokane	printf("3dfx: Opened by #%d\n", p->p_pid);
37061911Scokane#endif
37161911Scokane	/* Set the driver as busy */
37261911Scokane	tdfx_info->busy++;
37361911Scokane	return 0;
37461911Scokane}
37561911Scokane
37661911Scokanestatic int
37761911Scokanetdfx_close(dev_t dev, int fflag, int devtype, struct proc* p)
37861911Scokane{
37961911Scokane	/*
38061911Scokane	 *	The close cdev method handles close(2) calls to /dev/3dfx[n]
38161911Scokane	 * We'll always want to close the device when it's called.
38261911Scokane	 */
38361911Scokane	struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass,
38461911Scokane		UNIT(minor(dev)));
38561911Scokane	if(tdfx_info->busy == 0) return EBADF;
38661911Scokane	tdfx_info->busy = 0;
38761931Scokane#ifdef	DEBUG
38861911Scokane	printf("Closed by #%d\n", p->p_pid);
38961911Scokane#endif
39061911Scokane	return 0;
39161911Scokane}
39261911Scokane
39361911Scokanestatic int
39461911Scokanetdfx_mmap(dev_t dev, vm_offset_t offset, int nprot)
39561911Scokane{
39661911Scokane	/*
39761911Scokane	 * mmap(2) is called by a user process to request that an area of memory
39861911Scokane	 * associated with this device be mapped for the process to work with. Nprot
39961911Scokane	 * holds the protections requested, PROT_READ, PROT_WRITE, or both.
40061911Scokane	 */
40161911Scokane	struct tdfx_softc* tdfx_info;
40261911Scokane
40361911Scokane	/* Get the configuration for our card XXX*/
40461911Scokane	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
40561911Scokane			UNIT(minor(dev)));
40661911Scokane
40761911Scokane	/* If, for some reason, its not configured, we bail out */
40861911Scokane	if(tdfx_info == NULL) {
40961931Scokane#ifdef	DEBUG
41061911Scokane	   printf("tdfx: tdfx_info (softc) is NULL\n");
41161911Scokane#endif
41261911Scokane	   return -1;
41361911Scokane	}
41461911Scokane
41561911Scokane	/* We must stay within the bound of our address space */
41661911Scokane	if((offset & 0xff000000) == tdfx_info->addr0)
41761911Scokane		offset &= 0xffffff;
41861911Scokane	if((offset >= 0x1000000) || (offset < 0)) {
41961931Scokane#ifdef  DEBUG
42061911Scokane	   printf("tdfx: offset %x out of range\n", offset);
42161911Scokane#endif
42261911Scokane	   return -1;
42361911Scokane	}
42461911Scokane
42561911Scokane	/* atop -> address to page
42661911Scokane	 * rman_get_start, get the (struct resource*)->r_start member,
42761911Scokane	 * the mapping base address.
42861911Scokane	 */
42961911Scokane	return atop(rman_get_start(tdfx_info->memrange) + offset);
43061911Scokane}
43161911Scokane
43261911Scokanestatic int
43361911Scokanetdfx_query_boards(void) {
43461911Scokane	/*
43561911Scokane    *	This returns the number of installed tdfx cards, we have been keeping
43661911Scokane	 * count, look at tdfx_attach
43761911Scokane	 */
43861911Scokane	return tdfx_count;
43961911Scokane}
44061911Scokane
44161911Scokanestatic int
44261911Scokanetdfx_query_fetch(u_int cmd, struct tdfx_pio_data *piod)
44361911Scokane{
44461911Scokane	/* XXX Comment this later, after careful inspection and spring cleaning :) */
44561911Scokane	/* Various return values 8bit-32bit */
44661911Scokane	u_int8_t  ret_byte;
44761911Scokane	u_int16_t ret_word;
44861911Scokane	u_int32_t ret_dword;
44961911Scokane	struct tdfx_softc* tdfx_info = NULL;
45061911Scokane
45161911Scokane	/* This one depend on the tdfx_* structs being properly initialized */
45261911Scokane
45361911Scokane	/*piod->device &= 0xf;*/
45461911Scokane	if((piod == NULL) ||(tdfx_count <= piod->device) ||
45561911Scokane			(piod->device < 0)) {
45661931Scokane#ifdef DEBUG
45761911Scokane		printf("tdfx: Bad device or internal struct in tdfx_query_fetch\n");
45861911Scokane#endif
45961911Scokane		return -EINVAL;
46061911Scokane	}
46161911Scokane
46261911Scokane	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
46361911Scokane			piod->device);
46461911Scokane
46561911Scokane	if(tdfx_info == NULL) return -ENXIO;
46661911Scokane
46761911Scokane	/* We must restrict the size reads from the port, since to high or low of a
46861911Scokane	 * size witll result in wrong data being passed, and that's bad */
46961911Scokane	/* A few of these were pulled during the attach phase */
47061911Scokane	switch(piod->port) {
47161911Scokane		case PCI_VENDOR_ID_FREEBSD:
47261911Scokane			if(piod->size != 2) return -EINVAL;
47361911Scokane			copyout(&tdfx_info->vendor, piod->value, piod->size);
47461911Scokane			return 0;
47561911Scokane		case PCI_DEVICE_ID_FREEBSD:
47661911Scokane			if(piod->size != 2) return -EINVAL;
47761911Scokane			copyout(&tdfx_info->type, piod->value, piod->size);
47861911Scokane			return 0;
47961911Scokane		case PCI_BASE_ADDRESS_0_FREEBSD:
48061911Scokane			if(piod->size != 4) return -EINVAL;
48161911Scokane			copyout(&tdfx_info->addr0, piod->value, piod->size);
48261911Scokane			return 0;
48361911Scokane		case SST1_PCI_SPECIAL1_FREEBSD:
48461911Scokane			if(piod->size != 4) return -EINVAL;
48561911Scokane			break;
48661911Scokane		case PCI_REVISION_ID_FREEBSD:
48761911Scokane			if(piod->size != 1) return -EINVAL;
48861911Scokane			break;
48961911Scokane		case SST1_PCI_SPECIAL4_FREEBSD:
49061911Scokane			if(piod->size != 4) return -EINVAL;
49161911Scokane			break;
49261911Scokane		default:
49361911Scokane			return -EINVAL;
49461911Scokane	}
49561911Scokane
49661911Scokane
49761911Scokane	/* Read the value and return */
49861911Scokane	switch(piod->size) {
49961911Scokane		case 1:
50061911Scokane			ret_byte = pci_read_config(tdfx_info[piod->device].dev,
50161911Scokane					piod->port, 1);
50261911Scokane			copyout(&ret_byte, piod->value, 1);
50361911Scokane			break;
50461911Scokane		case 2:
50561911Scokane			ret_word = pci_read_config(tdfx_info[piod->device].dev,
50661911Scokane					piod->port, 2);
50761911Scokane			copyout(&ret_word, piod->value, 2);
50861911Scokane			break;
50961911Scokane		case 4:
51061911Scokane			ret_dword = pci_read_config(tdfx_info[piod->device].dev,
51161911Scokane					piod->port, 4);
51261911Scokane			copyout(&ret_dword, piod->value, 4);
51361911Scokane			break;
51461911Scokane		default:
51561911Scokane			return -EINVAL;
51661911Scokane	}
51761911Scokane	return 0;
51861911Scokane}
51961911Scokane
52061911Scokanestatic int
52161911Scokanetdfx_query_update(u_int cmd, struct tdfx_pio_data *piod)
52261911Scokane{
52361911Scokane	/* XXX Comment this later, after careful inspection and spring cleaning :) */
52461911Scokane	/* Return vals */
52561911Scokane	u_int8_t  ret_byte;
52661911Scokane	u_int16_t ret_word;
52761911Scokane	u_int32_t ret_dword;
52861911Scokane
52961911Scokane	/* Port vals, mask */
53061911Scokane	u_int32_t retval, preval, mask;
53161911Scokane	struct tdfx_softc* tdfx_info = NULL;
53261911Scokane
53361911Scokane
53461911Scokane	if((piod == NULL) || (piod->device >= (tdfx_count &
53561911Scokane					0xf))) {
53661931Scokane#ifdef DEBUG
53761911Scokane		printf("tdfx: Bad struct or device in tdfx_query_update\n");
53861911Scokane#endif
53961911Scokane		return -EINVAL;
54061911Scokane	}
54161911Scokane
54261911Scokane	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
54361911Scokane			piod->device);
54461911Scokane	if(tdfx_info == NULL) return -ENXIO;
54561911Scokane	/* Code below this line in the fuction was taken from the
54661911Scokane	 * Linux driver and converted for freebsd. */
54761911Scokane
54861911Scokane	/* Check the size for all the ports, to make sure stuff doesn't get messed up
54961911Scokane	 * by poorly written clients */
55061911Scokane
55161911Scokane	switch(piod->port) {
55261911Scokane		case PCI_COMMAND_FREEBSD:
55361911Scokane			if(piod->size != 2) return -EINVAL;
55461911Scokane			break;
55561911Scokane		case SST1_PCI_SPECIAL1_FREEBSD:
55661911Scokane			if(piod->size != 4) return -EINVAL;
55761911Scokane			break;
55861911Scokane		case SST1_PCI_SPECIAL2_FREEBSD:
55961911Scokane			if(piod->size != 4) return -EINVAL;
56061911Scokane			break;
56161911Scokane		case SST1_PCI_SPECIAL3_FREEBSD:
56261911Scokane			if(piod->size != 4) return -EINVAL;
56361911Scokane			break;
56461911Scokane		case SST1_PCI_SPECIAL4_FREEBSD:
56561911Scokane			if(piod->size != 4) return -EINVAL;
56661911Scokane			break;
56761911Scokane		default:
56861911Scokane			return -EINVAL;
56961911Scokane	}
57061911Scokane	/* Read the current value */
57161911Scokane	retval = pci_read_config(tdfx_info->dev, piod->port & ~3, 4);
57261911Scokane
57361911Scokane	/* These set up a mask to use, since apparently they wanted to write 4 bytes
57461911Scokane	 * at once to the ports */
57561911Scokane	switch (piod->size) {
57661911Scokane		case 1:
57761911Scokane			copyin(piod->value, &ret_byte, 1);
57861911Scokane			preval = ret_byte << (8 * (piod->port & 0x3));
57961911Scokane			mask = 0xff << (8 * (piod->port & 0x3));
58061911Scokane			break;
58161911Scokane		case 2:
58261911Scokane			copyin(piod->value, &ret_word, 2);
58361911Scokane			preval = ret_word << (8 * (piod->port & 0x3));
58461911Scokane			mask = 0xffff << (8 * (piod->port & 0x3));
58561911Scokane			break;
58661911Scokane		case 4:
58761911Scokane			copyin(piod->value, &ret_dword, 4);
58861911Scokane			preval = ret_dword;
58961911Scokane			mask = ~0;
59061911Scokane			break;
59161911Scokane		default:
59261911Scokane			return -EINVAL;
59361911Scokane	}
59461911Scokane	/* Finally, combine the values and write it to the port */
59561911Scokane	retval = (retval & ~mask) | preval;
59661911Scokane	pci_write_config(tdfx_info->dev, piod->port & ~3, retval, 4);
59761911Scokane
59861911Scokane	return 0;
59961911Scokane}
60061911Scokane
60161911Scokanestatic int
60261911Scokanetdfx_do_pio_rd(struct tdfx_pio_data *piod)
60361911Scokane{
60461911Scokane	/* Return val */
60561911Scokane	u_int8_t  ret_byte;
60661911Scokane
60761911Scokane	/* Restricts the access of ports other than those we use */
60861911Scokane	if((piod->port != VGA_INPUT_STATUS_1C) || (piod->port != SC_INDEX) ||
60961911Scokane		(piod->port != SC_DATA) || (piod->port != VGA_MISC_OUTPUT_READ))
61061911Scokane		return -EPERM;
61161911Scokane
61261911Scokane	/* All VGA STATUS REGS are byte registers, size should never be > 1 */
61361911Scokane	if(piod->size != 1) {
61461911Scokane		return -EINVAL;
61561911Scokane	}
61661911Scokane
61761911Scokane	/* Write the data to the intended port */
61861911Scokane	ret_byte = inb(piod->port);
61961911Scokane	copyout(&ret_byte, piod->value, sizeof(u_int8_t));
62061911Scokane	return 0;
62161911Scokane}
62261911Scokane
62361911Scokanestatic int
62461911Scokanetdfx_do_pio_wt(struct tdfx_pio_data *piod)
62561911Scokane{
62661911Scokane	/* return val */
62761911Scokane	u_int8_t  ret_byte;
62861911Scokane
62961911Scokane	/* Replace old switch w/ massive if(...) */
63061911Scokane	/* Restricts the access of ports other than those we use */
63161911Scokane	if((piod->port != SC_INDEX) && (piod->port != SC_DATA) &&
63261911Scokane		(piod->port != VGA_MISC_OUTPUT_READ)) /* Can't write VGA_ST_1C */
63361911Scokane		return -EPERM;
63461911Scokane
63561911Scokane	/* All VGA STATUS REGS are byte registers, size should never be > 1 */
63661911Scokane	if(piod->size != 1) {
63761911Scokane		return -EINVAL;
63861911Scokane	}
63961911Scokane
64061911Scokane	/* Write the data to the intended port */
64161911Scokane	copyin(piod->value, &ret_byte, sizeof(u_int8_t));
64261911Scokane	outb(piod->port, ret_byte);
64361911Scokane	return 0;
64461911Scokane}
64561911Scokane
64661911Scokanestatic int
64761911Scokanetdfx_do_query(u_int cmd, struct tdfx_pio_data *piod)
64861911Scokane{
64961911Scokane	/* There are three sub-commands to the query 0x33 */
65061911Scokane	switch(_IOC_NR(cmd)) {
65161911Scokane		case 2:
65261911Scokane			return tdfx_query_boards();
65361911Scokane			break;
65461911Scokane		case 3:
65561911Scokane			return tdfx_query_fetch(cmd, piod);
65661911Scokane			break;
65761911Scokane		case 4:
65861911Scokane			return tdfx_query_update(cmd, piod);
65961911Scokane			break;
66061911Scokane		default:
66161911Scokane			/* In case we are thrown a bogus sub-command! */
66261931Scokane#ifdef DEBUG
66361911Scokane			printf("Bad Sub-cmd: 0x%x\n", _IOC_NR(cmd));
66461911Scokane#endif
66561911Scokane			return -EINVAL;
66661911Scokane	};
66761911Scokane}
66861911Scokane
66961911Scokanestatic int
67061911Scokanetdfx_do_pio(u_int cmd, struct tdfx_pio_data *piod)
67161911Scokane{
67261911Scokane	/* Two types of PIO, INPUT and OUTPUT, as the name suggests */
67361911Scokane	switch(_IOC_DIR(cmd)) {
67461911Scokane		case IOCV_OUT:
67561911Scokane			return tdfx_do_pio_rd(piod);
67661911Scokane			break;
67761911Scokane		case IOCV_IN:
67861911Scokane			return tdfx_do_pio_wt(piod);
67961911Scokane			break;
68061911Scokane		default:
68161911Scokane			return -EINVAL;
68261911Scokane	};
68361911Scokane}
68461911Scokane
68561911Scokane/* Calls to ioctl(2) eventually end up here. Unhandled ioctls return an ENXIO,
68661911Scokane * normally, you would read in the data pointed to by data, then write your
68761911Scokane * output to it. The ioctl *should* normally return zero if everything is
68861911Scokane * alright, but 3dfx didn't make it that way...
68961911Scokane *
69061911Scokane * For all of the ioctl code, in the event of a real error,
69161911Scokane * we return -Exxxx rather than simply Exxxx. The reason for this
69261911Scokane * is that the ioctls actually RET information back to the program
69361911Scokane * sometimes, rather than filling it in the passed structure. We
69461911Scokane * want to distinguish errors from useful data, and maintain compatibility.
69561911Scokane *
69661911Scokane * There is this portion of the proc struct called p_retval[], we can store a
69761911Scokane * return value in p->p_retval[0] and place the return value if it is positive
69861911Scokane * in there, then we can return 0 (good). If the return value is negative, we
69961911Scokane * can return -retval and the error should be properly handled.
70061911Scokane */
70161911Scokanestatic int
70261911Scokanetdfx_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc * p)
70361911Scokane{
70461911Scokane	int retval = 0;
70561911Scokane	struct tdfx_pio_data *piod = (struct tdfx_pio_data*)data;
70661931Scokane#ifdef	DEBUG
70761911Scokane	printf("IOCTL'd by #%d, cmd: 0x%x, data: 0x%x\n", p->p_pid, (u_int32_t)cmd,
70861911Scokane			(unsigned int)piod);
70961911Scokane#endif
71061911Scokane	switch(_IOC_TYPE(cmd)) {
71161911Scokane		/* Return the real error if negative, or simply stick the valid return
71261911Scokane		 * in p->p_retval */
71361911Scokane	case 0x33:
71461911Scokane			/* The '3'(0x33) type IOCTL is for querying the installed cards */
71561911Scokane			if((retval = tdfx_do_query(cmd, piod)) > 0) p->p_retval[0] = retval;
71661911Scokane			else return -retval;
71761911Scokane			break;
71861911Scokane		case 0:
71961911Scokane			/* The 0 type IOCTL is for programmed I/O methods */
72061911Scokane			if((tdfx_do_pio(cmd, piod)) > 0) p->p_retval[0] = retval;
72161911Scokane			else return -retval;
72261911Scokane			break;
72361911Scokane		default:
72461911Scokane			/* Technically, we won't reach this from linux emu, but when glide
72561911Scokane			 * finally gets ported, watch out! */
72661931Scokane#ifdef DEBUG
72761911Scokane			printf("Bad IOCTL from #%d\n", p->p_pid);
72861911Scokane#endif
72961911Scokane			return ENXIO;
73061911Scokane	}
73161911Scokane
73261911Scokane	return 0;
73361911Scokane}
73461911Scokane
73561931Scokane#ifdef TDFX_LINUX
73661931Scokane/*
73761931Scokane * Linux emulation IOCTL for /dev/tdfx
73861931Scokane */
73961931Scokanestatic int
74061931Scokanelinux_ioctl_tdfx(struct proc* p, struct linux_ioctl_args* args)
74161931Scokane{
74261931Scokane   int error = 0;
74361931Scokane   u_long cmd = args->cmd & 0xffff;
74461911Scokane
74561931Scokane   /* The structure passed to ioctl has two shorts, one int
74661931Scokane      and one void*. */
74761931Scokane   char d_pio[2*sizeof(short) + sizeof(int) + sizeof(void*)];
74861931Scokane
74961931Scokane   struct file *fp = p->p_fd->fd_ofiles[args->fd];
75061931Scokane
75161931Scokane   /* We simply copy the data and send it right to ioctl */
75261931Scokane   copyin((caddr_t)args->arg, &d_pio, sizeof(d_pio));
75361931Scokane   error = fo_ioctl(fp, cmd, (caddr_t)&d_pio, p);
75461931Scokane   return error;
75561931Scokane}
75661931Scokane#endif /* TDFX_LINUX */
75761931Scokane
75861931Scokane
75961911Scokane/* This is the device driver struct. This is sent to the driver subsystem to
76061911Scokane * register the method structure and the info strcut space for this particular
76161911Scokane * instance of the driver.
76261911Scokane */
76361911Scokanestatic driver_t tdfx_driver = {
76461911Scokane	"tdfx",
76561911Scokane	tdfx_methods,
76661911Scokane	sizeof(struct tdfx_softc),
76761911Scokane};
76861911Scokane
76961911Scokane/* Tell Mr. Kernel about us! */
77061911ScokaneDRIVER_MODULE(tdfx, pci, tdfx_driver, tdfx_devclass, 0, 0);
77161911Scokane
77261911Scokane
77361911Scokane#endif	/* NPCI */
774