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