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