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