From fd35136c6dcf725645029a03a946c73fb3467e67 Mon Sep 17 00:00:00 2001 From: cokane Date: Wed, 21 Jun 2000 20:09:31 +0000 Subject: First import of my 3dfx voodoo driver. Currently it supports the Voodoo Graphics and Voodoo2 perfectly. It works just like the 3dfx driver does for linux, by using a character device at /dev/3dfx of Major 107 to provide a window into the 3dfx card's memory space. This interface is used by glide and mesa as far as i know, and probably some other libraries too. Approved by: jkh --- sys/dev/tdfx/tdfx_io.h | 68 +++++ sys/dev/tdfx/tdfx_pci.c | 707 ++++++++++++++++++++++++++++++++++++++++++++ sys/dev/tdfx/tdfx_pci.h | 29 ++ sys/dev/tdfx/tdfx_static.sh | 2 + sys/dev/tdfx/tdfx_vars.h | 70 +++++ 5 files changed, 876 insertions(+) create mode 100644 sys/dev/tdfx/tdfx_io.h create mode 100644 sys/dev/tdfx/tdfx_pci.c create mode 100644 sys/dev/tdfx/tdfx_pci.h create mode 100644 sys/dev/tdfx/tdfx_static.sh create mode 100644 sys/dev/tdfx/tdfx_vars.h (limited to 'sys/dev/tdfx') diff --git a/sys/dev/tdfx/tdfx_io.h b/sys/dev/tdfx/tdfx_io.h new file mode 100644 index 0000000..47d7186 --- /dev/null +++ b/sys/dev/tdfx/tdfx_io.h @@ -0,0 +1,68 @@ +/* This code originally came from a nice man, I don't have your name, + * please email cokane@pohl.ececs.uc.edu for proper recognition + * it is basically a derivative of some sys/dhio.h file to work with the 3dfx + * cards + */ +#ifndef TDFX_IO_H +#define TDFX_IO_H + +#ifndef KERNEL +#include +#endif + +/* + * define an ioctl here + */ +#define DHIOCRESET _IO('D', 0) /* reset the voodoo device */ +#define GETVOODOO 0x3302 +#define SETVOODOO 0x3303 +#define MOREVOODOO 0x3300 +#define _IOC_NRBITS 8 +#define _IOC_TYPEBITS 8 +#define _IOC_SIZEBITS 14 +#define _IOC_DIRBITS 2 + +#define _IOC_NRMASK ((1 << _IOC_NRBITS)-1) +#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1) +#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1) +#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1) + +#define _IOC_NRSHIFT 0 +#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) +#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS) +#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS) + +/* + * Direction bits. + */ +#define _IOC_NONE 0U +#define _IOC_WRITE 1U +#define _IOC_READ 2U + +#define _IOCV(dir,type,nr,size) \ + (((dir) << _IOC_DIRSHIFT) | \ + ((type) << _IOC_TYPESHIFT) | \ + ((nr) << _IOC_NRSHIFT) | \ + ((size) << _IOC_SIZESHIFT)) + +/* used to create numbers */ +#define _IOV(type,nr) _IOCV(_IOC_NONE,(type),(nr),0) +#define _IORV(type,nr,size) _IOCV(_IOC_READ,(type),(nr),sizeof(size)) +#define _IOWV(type,nr,size) _IOCV(_IOC_WRITE,(type),(nr),sizeof(size)) +#define _IOWRV(type,nr,size) _IOCV(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size)) + +/* used to decode ioctl numbers.. */ +#define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK) +#define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK) +#define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK) +#define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK) + +/* ...and for the drivers/sound files... */ + +#define IOCV_IN (_IOC_WRITE << _IOC_DIRSHIFT) +#define IOCV_OUT (_IOC_READ << _IOC_DIRSHIFT) +#define IOCV_INOUT ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT) +#define IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT) +#define IOCSIZE_SHIFT (_IOC_SIZESHIFT) + +#endif diff --git a/sys/dev/tdfx/tdfx_pci.c b/sys/dev/tdfx/tdfx_pci.c new file mode 100644 index 0000000..976628d --- /dev/null +++ b/sys/dev/tdfx/tdfx_pci.c @@ -0,0 +1,707 @@ +/* 3dfx driver for FreeBSD 4.x - Finished 11 May 2000, 12:25AM ET + * + * Copyright (C) 2000, by Coleman Kane , + * based upon the 3dfx driver written for linux, by Daryll Straus, Jon Taylor, + * and Jens Axboe, located at http://linux.3dfx.com. + */ + +/* + * put this here, so as to bail out immediately if we have no PCI BUS installed + */ +#include "pci.h" +#if NPCI > 0 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/*#include */ +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +/* rman.h depends on machine/bus.h */ +#include +#include +#include + +#include +#include +#include + +#include "opt_tdfx.h" + +static devclass_t tdfx_devclass; + + +static int tdfx_count = 0; + + +/* Set up the boot probe/attach routines */ +static device_method_t tdfx_methods[] = { + DEVMETHOD(device_probe, tdfx_probe), + DEVMETHOD(device_attach, tdfx_attach), + DEVMETHOD(device_detach, tdfx_detach), + DEVMETHOD(device_shutdown, tdfx_shutdown), + { 0, 0 } +}; + +MALLOC_DEFINE(M_TDFX,"TDFX Driver","3DFX Graphics[/2D]/3D Accelerator(s)"); + +/* Char. Dev. file operations structure */ +static struct cdevsw tdfx_cdev = { + tdfx_open, /* open */ + tdfx_close, /* close */ + noread, /* read */ + nowrite, /* write */ + tdfx_ioctl, /* ioctl */ + nopoll, /* poll */ + tdfx_mmap, /* mmap */ + nostrategy, /* strategy */ + "tdfx", /* dev name */ + CDEV_MAJOR, /* char major */ + nodump, /* dump */ + nopsize, /* size */ + 0, /* flags (no set flags) */ + -1 /* bmaj (no block dev) */ +}; + +static int +tdfx_probe(device_t dev) +{ + /* + * probe routine called on kernel boot to register supported devices. We get + * a device structure to work with, and we can test the VENDOR/DEVICE IDs to + * see if this PCI device is one that we support. Return 0 if yes, ENXIO if + * not. + */ + switch(pci_get_devid(dev)) { + case PCI_DEVICE_ALLIANCE_AT3D: + device_set_desc(dev, "ProMotion At3D 3D Accelerator"); + return 0; + case PCI_DEVICE_3DFX_VOODOO2: + device_set_desc(dev, "3DFX Voodoo II 3D Accelerator"); + return 0; + case PCI_DEVICE_3DFX_BANSHEE: + device_set_desc(dev, "3DFX Voodoo Banshee 2D/3D Graphics Accelerator"); + return 0; + case PCI_DEVICE_3DFX_VOODOO3: + device_set_desc(dev, "3DFX Voodoo3 2D/3D Graphics Accelerator"); + return 0; + case PCI_DEVICE_3DFX_VOODOO1: + device_set_desc(dev, "3DFX Voodoo Graphics 3D Accelerator"); + return 0;; + }; + + return ENXIO; +} + +static int +tdfx_attach(device_t dev) { + /* + * The attach routine is called after the probe routine successfully says it + * supports a given card. We now proceed to initialize this card for use with + * the system. I want to map the device memory for userland allocation and + * fill an information structure with information on this card. I'd also like + * to set Write Combining with the MTRR code so that we can hopefully speed + * up memory writes. The last thing is to register the character device + * interface to the card, so we can open it from /dev/3dfxN, where N is a + * small, whole number. + */ + + struct tdfx_softc *tdfx_info; + u_long val; + /* rid value tells bus_alloc_resource where to find the addresses of ports or + * of memory ranges in the PCI config space*/ + int rid = PCIR_MAPS; + + /* Increment the card counter (for the ioctl code) */ + tdfx_count++; + + /* Enable MemMap on Voodoo */ + val = pci_read_config(dev, PCIR_COMMAND, 2); + val |= (PCIM_CMD_MEMEN); + pci_write_config(dev, PCIR_COMMAND, val, 2); + val = pci_read_config(dev, PCIR_COMMAND, 2); + + /* Fill the soft config struct with info about this device*/ + tdfx_info = device_get_softc(dev); + tdfx_info->dev = dev; + tdfx_info->vendor = pci_get_vendor(dev); + tdfx_info->type = pci_get_devid(dev) >> 16; + tdfx_info->bus = pci_get_bus(dev); + tdfx_info->dv = pci_get_slot(dev); + tdfx_info->curFile = NULL; + + /* + * Get the Memory Location from the PCI Config, mask out lower word, since + * the config space register is only one word long (this is nicer than a + * bitshift). + */ + tdfx_info->addr0 = (pci_read_config(dev, 0x10, 4) & 0xffff0000); +#ifdef TDFX_VERBOSE + device_printf(dev, "Base0 @ 0x%x\n", tdfx_info->addr0); +#endif + + /* Notify the VM that we will be mapping some memory later */ + tdfx_info->memrange = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, ~0, 1, + RF_ACTIVE); + if(tdfx_info->memrange == NULL) { +#ifdef TDFX_VERBOSE + device_printf(dev, "Error mapping mem, won't be able to use mmap()\n"); +#endif + tdfx_info->memrid = 0; + } + else { + tdfx_info->memrid = rid; +#ifdef TDFX_VERBOSE + device_printf(dev, "Mapped to: 0x%x\n", + (unsigned int)rman_get_start(tdfx_info->memrange)); +#endif + } + + /* + * Set Writecombining, or at least Uncacheable for the memory region, if we + * are able to + */ + + if(tdfx_setmtrr(dev) != 0) { +#ifdef TDFX_VERBOSE + device_printf(dev, "Some weird error setting MTRRs"); +#endif + return -1; + } + + /* + * make_dev registers the cdev to access the 3dfx card from /dev + * use hex here for the dev num, simply to provide better support if > 10 + * voodoo cards, for the mad. The user must set the link, or use MAKEDEV. + * Why would we want that many voodoo cards anyhow? + */ + make_dev(&tdfx_cdev, dev->unit, 0, 0, 02660, "3dfx%x", dev->unit); + + return 0; +} + +static int +tdfx_detach(device_t dev) { + struct tdfx_softc* tdfx_info; + int retval; + tdfx_info = device_get_softc(dev); + + /* Delete allocated resource, of course */ + bus_release_resource(dev, SYS_RES_MEMORY, PCI_MAP_REG_START, + tdfx_info->memrange); + + /* Though it is safe to leave the WRCOMB support since the + mem driver checks for it, we should remove it in order + to free an MTRR for another device */ + retval = tdfx_clrmtrr(dev); +#ifdef TDFX_VERBOSE + if(retval != 0) + printf("tdfx: For some reason, I couldn't clear the mtrr\n"); +#endif + return(0); +} + +static int +tdfx_shutdown(device_t dev) { +#ifdef TDFX_VERBOSE + device_printf(dev, "tdfx: Device Shutdown\n"); +#endif + return 0; +} + +static int +tdfx_clrmtrr(device_t dev) { + /* This function removes the MTRR set by the attach call, so it can be used + * in the future by other drivers. + */ + int retval, act; + struct tdfx_softc *tdfx_info = device_get_softc(dev); + + act = MEMRANGE_SET_REMOVE; + retval = mem_range_attr_set(&tdfx_info->mrdesc, &act); + return retval; +} + +static int +tdfx_setmtrr(device_t dev) { + /* + * This is the MTRR setting function for the 3dfx card. It is called from + * tdfx_attach. If we can't set the MTRR properly, it's not the end of the + * world. We can still continue, just with slightly (very slightly) degraded + * performance. + */ + int retval = 0, act; + struct tdfx_softc *tdfx_info = device_get_softc(dev); + /* The memory descriptor is described as the top 15 bits of the real + address */ + tdfx_info->mrdesc.mr_base = pci_read_config(dev, 0x10, 4) & 0xfffe0000; + + /* The older Voodoo cards have a shorter memrange than the newer ones */ + if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1) || (pci_get_devid(dev) == + PCI_DEVICE_3DFX_VOODOO2)) + tdfx_info->mrdesc.mr_len = 0x400000; + else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) || + (pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE)) + tdfx_info->mrdesc.mr_len = 0x1000000; + + else return 0; + /* + * The Alliance Pro Motion AT3D was not mentioned in the linux + * driver as far as MTRR support goes, so I just won't put the + * code in here for it. This is where it should go, though. + */ + + /* Firstly, try to set write combining */ + tdfx_info->mrdesc.mr_flags = MDF_WRITECOMBINE; + bcopy("tdfx", &tdfx_info->mrdesc.mr_owner, 4); + act = MEMRANGE_SET_UPDATE; + retval = mem_range_attr_set(&tdfx_info->mrdesc, &act); + + if(retval == 0) { +#ifdef TDFX_VERBOSE + device_printf(dev, "MTRR Set Correctly for tdfx\n"); +#endif + } else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO2) || + (pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1)) { + /* if, for some reason we can't set the WRCOMB range with the V1/V2, we + * can still possibly use the UNCACHEABLE region for it instead, and help + * out in a small way */ + tdfx_info->mrdesc.mr_flags = MDF_UNCACHEABLE; + /* This length of 1000h was taken from the linux device driver... */ + tdfx_info->mrdesc.mr_len = 0x1000; + + /* + * If, for some reason, we can't set the MTRR (N/A?) we may still continue + */ +#ifdef TDFX_VERBOSE + if(retval == 0) { + device_printf(dev, "MTRR Set Type Uncacheable + %x\n", (u_int32_t)tdfx_info->mrdesc.mr_base); + } else { + device_printf(dev, "Couldn't Set MTRR\n"); + } +#endif + } +#ifdef TDFX_VERBOSE + else { + device_printf(dev, "Couldn't Set MTRR\n"); + return 0; + } +#endif + return 0; +} + +static int +tdfx_open(dev_t dev, int flags, int fmt, struct proc *p) +{ + /* + * The open cdev method handles open(2) calls to /dev/3dfx[n] + * We can pretty much allow any opening of the device. + */ + struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass, + UNIT(minor(dev))); + if(tdfx_info->busy != 0) return EBUSY; +#ifdef TDFX_VERBOSE + printf("3dfx: Opened by #%d\n", p->p_pid); +#endif + /* Set the driver as busy */ + tdfx_info->busy++; + return 0; +} + +static int +tdfx_close(dev_t dev, int fflag, int devtype, struct proc* p) +{ + /* + * The close cdev method handles close(2) calls to /dev/3dfx[n] + * We'll always want to close the device when it's called. + */ + struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass, + UNIT(minor(dev))); + if(tdfx_info->busy == 0) return EBADF; + tdfx_info->busy = 0; +#ifdef TDFX_VERBOSE + printf("Closed by #%d\n", p->p_pid); +#endif + return 0; +} + +static int +tdfx_mmap(dev_t dev, vm_offset_t offset, int nprot) +{ + /* + * mmap(2) is called by a user process to request that an area of memory + * associated with this device be mapped for the process to work with. Nprot + * holds the protections requested, PROT_READ, PROT_WRITE, or both. + */ + struct tdfx_softc* tdfx_info; + + /* Get the configuration for our card XXX*/ + tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, + UNIT(minor(dev))); + + /* If, for some reason, its not configured, we bail out */ + if(tdfx_info == NULL) { +#ifdef TDFX_VERBOSE + printf("tdfx: tdfx_info (softc) is NULL\n"); +#endif + return -1; + } + + /* We must stay within the bound of our address space */ + if((offset & 0xff000000) == tdfx_info->addr0) + offset &= 0xffffff; + if((offset >= 0x1000000) || (offset < 0)) { +#ifdef TDFX_VERBOSE + printf("tdfx: offset %x out of range\n", offset); +#endif + return -1; + } + + /* atop -> address to page + * rman_get_start, get the (struct resource*)->r_start member, + * the mapping base address. + */ + return atop(rman_get_start(tdfx_info->memrange) + offset); +} + +static int +tdfx_query_boards(void) { + /* + * This returns the number of installed tdfx cards, we have been keeping + * count, look at tdfx_attach + */ + return tdfx_count; +} + +static int +tdfx_query_fetch(u_int cmd, struct tdfx_pio_data *piod) +{ + /* XXX Comment this later, after careful inspection and spring cleaning :) */ + /* Various return values 8bit-32bit */ + u_int8_t ret_byte; + u_int16_t ret_word; + u_int32_t ret_dword; + struct tdfx_softc* tdfx_info = NULL; + + /* This one depend on the tdfx_* structs being properly initialized */ + + /*piod->device &= 0xf;*/ + if((piod == NULL) ||(tdfx_count <= piod->device) || + (piod->device < 0)) { +#ifdef TDFX_VERBOSE + printf("tdfx: Bad device or internal struct in tdfx_query_fetch\n"); +#endif + return -EINVAL; + } + + tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, + piod->device); + + if(tdfx_info == NULL) return -ENXIO; + + /* We must restrict the size reads from the port, since to high or low of a + * size witll result in wrong data being passed, and that's bad */ + /* A few of these were pulled during the attach phase */ + switch(piod->port) { + case PCI_VENDOR_ID_FREEBSD: + if(piod->size != 2) return -EINVAL; + copyout(&tdfx_info->vendor, piod->value, piod->size); + return 0; + case PCI_DEVICE_ID_FREEBSD: + if(piod->size != 2) return -EINVAL; + copyout(&tdfx_info->type, piod->value, piod->size); + return 0; + case PCI_BASE_ADDRESS_0_FREEBSD: + if(piod->size != 4) return -EINVAL; + copyout(&tdfx_info->addr0, piod->value, piod->size); + return 0; + case SST1_PCI_SPECIAL1_FREEBSD: + if(piod->size != 4) return -EINVAL; + break; + case PCI_REVISION_ID_FREEBSD: + if(piod->size != 1) return -EINVAL; + break; + case SST1_PCI_SPECIAL4_FREEBSD: + if(piod->size != 4) return -EINVAL; + break; + default: + return -EINVAL; + } + + + /* Read the value and return */ + switch(piod->size) { + case 1: + ret_byte = pci_read_config(tdfx_info[piod->device].dev, + piod->port, 1); + copyout(&ret_byte, piod->value, 1); + break; + case 2: + ret_word = pci_read_config(tdfx_info[piod->device].dev, + piod->port, 2); + copyout(&ret_word, piod->value, 2); + break; + case 4: + ret_dword = pci_read_config(tdfx_info[piod->device].dev, + piod->port, 4); + copyout(&ret_dword, piod->value, 4); + break; + default: + return -EINVAL; + } + return 0; +} + +static int +tdfx_query_update(u_int cmd, struct tdfx_pio_data *piod) +{ + /* XXX Comment this later, after careful inspection and spring cleaning :) */ + /* Return vals */ + u_int8_t ret_byte; + u_int16_t ret_word; + u_int32_t ret_dword; + + /* Port vals, mask */ + u_int32_t retval, preval, mask; + struct tdfx_softc* tdfx_info = NULL; + + + if((piod == NULL) || (piod->device >= (tdfx_count & + 0xf))) { +#ifdef TDFX_VERBOSE + printf("tdfx: Bad struct or device in tdfx_query_update\n"); +#endif + return -EINVAL; + } + + tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, + piod->device); + if(tdfx_info == NULL) return -ENXIO; + /* Code below this line in the fuction was taken from the + * Linux driver and converted for freebsd. */ + + /* Check the size for all the ports, to make sure stuff doesn't get messed up + * by poorly written clients */ + + switch(piod->port) { + case PCI_COMMAND_FREEBSD: + if(piod->size != 2) return -EINVAL; + break; + case SST1_PCI_SPECIAL1_FREEBSD: + if(piod->size != 4) return -EINVAL; + break; + case SST1_PCI_SPECIAL2_FREEBSD: + if(piod->size != 4) return -EINVAL; + break; + case SST1_PCI_SPECIAL3_FREEBSD: + if(piod->size != 4) return -EINVAL; + break; + case SST1_PCI_SPECIAL4_FREEBSD: + if(piod->size != 4) return -EINVAL; + break; + default: + return -EINVAL; + } + /* Read the current value */ + retval = pci_read_config(tdfx_info->dev, piod->port & ~3, 4); + + /* These set up a mask to use, since apparently they wanted to write 4 bytes + * at once to the ports */ + switch (piod->size) { + case 1: + copyin(piod->value, &ret_byte, 1); + preval = ret_byte << (8 * (piod->port & 0x3)); + mask = 0xff << (8 * (piod->port & 0x3)); + break; + case 2: + copyin(piod->value, &ret_word, 2); + preval = ret_word << (8 * (piod->port & 0x3)); + mask = 0xffff << (8 * (piod->port & 0x3)); + break; + case 4: + copyin(piod->value, &ret_dword, 4); + preval = ret_dword; + mask = ~0; + break; + default: + return -EINVAL; + } + /* Finally, combine the values and write it to the port */ + retval = (retval & ~mask) | preval; + pci_write_config(tdfx_info->dev, piod->port & ~3, retval, 4); + + return 0; +} + +static int +tdfx_do_pio_rd(struct tdfx_pio_data *piod) +{ + /* Return val */ + u_int8_t ret_byte; + + /* Restricts the access of ports other than those we use */ + if((piod->port != VGA_INPUT_STATUS_1C) || (piod->port != SC_INDEX) || + (piod->port != SC_DATA) || (piod->port != VGA_MISC_OUTPUT_READ)) + return -EPERM; + + /* All VGA STATUS REGS are byte registers, size should never be > 1 */ + if(piod->size != 1) { + return -EINVAL; + } + + /* Write the data to the intended port */ + ret_byte = inb(piod->port); + copyout(&ret_byte, piod->value, sizeof(u_int8_t)); + return 0; +} + +static int +tdfx_do_pio_wt(struct tdfx_pio_data *piod) +{ + /* return val */ + u_int8_t ret_byte; + + /* Replace old switch w/ massive if(...) */ + /* Restricts the access of ports other than those we use */ + if((piod->port != SC_INDEX) && (piod->port != SC_DATA) && + (piod->port != VGA_MISC_OUTPUT_READ)) /* Can't write VGA_ST_1C */ + return -EPERM; + + /* All VGA STATUS REGS are byte registers, size should never be > 1 */ + if(piod->size != 1) { + return -EINVAL; + } + + /* Write the data to the intended port */ + copyin(piod->value, &ret_byte, sizeof(u_int8_t)); + outb(piod->port, ret_byte); + return 0; +} + +static int +tdfx_do_query(u_int cmd, struct tdfx_pio_data *piod) +{ + /* There are three sub-commands to the query 0x33 */ + switch(_IOC_NR(cmd)) { + case 2: + return tdfx_query_boards(); + break; + case 3: + return tdfx_query_fetch(cmd, piod); + break; + case 4: + return tdfx_query_update(cmd, piod); + break; + default: + /* In case we are thrown a bogus sub-command! */ +#ifdef TDFX_VERBOSE + printf("Bad Sub-cmd: 0x%x\n", _IOC_NR(cmd)); +#endif + return -EINVAL; + }; +} + +static int +tdfx_do_pio(u_int cmd, struct tdfx_pio_data *piod) +{ + /* Two types of PIO, INPUT and OUTPUT, as the name suggests */ + switch(_IOC_DIR(cmd)) { + case IOCV_OUT: + return tdfx_do_pio_rd(piod); + break; + case IOCV_IN: + return tdfx_do_pio_wt(piod); + break; + default: + return -EINVAL; + }; +} + +/* Calls to ioctl(2) eventually end up here. Unhandled ioctls return an ENXIO, + * normally, you would read in the data pointed to by data, then write your + * output to it. The ioctl *should* normally return zero if everything is + * alright, but 3dfx didn't make it that way... + * + * For all of the ioctl code, in the event of a real error, + * we return -Exxxx rather than simply Exxxx. The reason for this + * is that the ioctls actually RET information back to the program + * sometimes, rather than filling it in the passed structure. We + * want to distinguish errors from useful data, and maintain compatibility. + * + * There is this portion of the proc struct called p_retval[], we can store a + * return value in p->p_retval[0] and place the return value if it is positive + * in there, then we can return 0 (good). If the return value is negative, we + * can return -retval and the error should be properly handled. + */ +static int +tdfx_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc * p) +{ + int retval = 0; + struct tdfx_pio_data *piod = (struct tdfx_pio_data*)data; +#ifdef TDFX_VERBOSE + printf("IOCTL'd by #%d, cmd: 0x%x, data: 0x%x\n", p->p_pid, (u_int32_t)cmd, + (unsigned int)piod); +#endif + switch(_IOC_TYPE(cmd)) { + /* Return the real error if negative, or simply stick the valid return + * in p->p_retval */ + case 0x33: + /* The '3'(0x33) type IOCTL is for querying the installed cards */ + if((retval = tdfx_do_query(cmd, piod)) > 0) p->p_retval[0] = retval; + else return -retval; + break; + case 0: + /* The 0 type IOCTL is for programmed I/O methods */ + if((tdfx_do_pio(cmd, piod)) > 0) p->p_retval[0] = retval; + else return -retval; + break; + default: + /* Technically, we won't reach this from linux emu, but when glide + * finally gets ported, watch out! */ +#ifdef TDFX_VERBOSE + printf("Bad IOCTL from #%d\n", p->p_pid); +#endif + return ENXIO; + } + + return 0; +} + + +/* This is the device driver struct. This is sent to the driver subsystem to + * register the method structure and the info strcut space for this particular + * instance of the driver. + */ +static driver_t tdfx_driver = { + "tdfx", + tdfx_methods, + sizeof(struct tdfx_softc), +}; + +/* Tell Mr. Kernel about us! */ +DRIVER_MODULE(tdfx, pci, tdfx_driver, tdfx_devclass, 0, 0); + + +#endif /* NPCI */ diff --git a/sys/dev/tdfx/tdfx_pci.h b/sys/dev/tdfx/tdfx_pci.h new file mode 100644 index 0000000..3fe5f53 --- /dev/null +++ b/sys/dev/tdfx/tdfx_pci.h @@ -0,0 +1,29 @@ +/* tdfx_pci.h -- Prototypes for tdfx device methods */ +/* Copyright (C) 2000 by Coleman Kane */ +#include +#include + +/* Driver functions */ +static int tdfx_probe(device_t dev); +static int tdfx_attach(device_t dev); +static int tdfx_setmtrr(device_t dev); +static int tdfx_clrmtrr(device_t dev); +static int tdfx_detach(device_t dev); +static int tdfx_shutdown(device_t dev); + +/* CDEV file ops */ +static d_open_t tdfx_open; +static d_close_t tdfx_close; +static d_mmap_t tdfx_mmap; +static d_ioctl_t tdfx_ioctl; + +/* Card Queries */ +static int tdfx_do_query(u_int cmd, struct tdfx_pio_data *piod); +static int tdfx_query_boards(void); +static int tdfx_query_fetch(u_int cmd, struct tdfx_pio_data *piod); +static int tdfx_query_update(u_int cmd, struct tdfx_pio_data *piod); + +/* Card PIO funcs */ +static int tdfx_do_pio(u_int cmd, struct tdfx_pio_data *piod); +static int tdfx_do_pio_wt(struct tdfx_pio_data *piod); +static int tdfx_do_pio_rd(struct tdfx_pio_data *piod); diff --git a/sys/dev/tdfx/tdfx_static.sh b/sys/dev/tdfx/tdfx_static.sh new file mode 100644 index 0000000..a238167 --- /dev/null +++ b/sys/dev/tdfx/tdfx_static.sh @@ -0,0 +1,2 @@ +echo "dev/tdfx/tdfx_pci.c optional tdfx pci" >> /usr/src/sys/conf/files.i386 +echo "TDFX_VERBOSE opt_tdfx.h" >> /usr/src/sys/conf/options.i386 diff --git a/sys/dev/tdfx/tdfx_vars.h b/sys/dev/tdfx/tdfx_vars.h new file mode 100644 index 0000000..13e386f --- /dev/null +++ b/sys/dev/tdfx/tdfx_vars.h @@ -0,0 +1,70 @@ +/* tdfx_vars.h -- constants and structs used in the tdfx driver + Copyright (C) 2000 by Coleman Kane +*/ +#ifndef TDFX_VARS_H +#define TDFX_VARS_H + +#include + +#define CDEV_MAJOR 107 +#define PCI_DEVICE_ALLIANCE_AT3D 0x643d1142 +#define PCI_DEVICE_3DFX_VOODOO1 0x0000121a +#define PCI_DEVICE_3DFX_VOODOO2 0x0002121a +#define PCI_DEVICE_3DFX_BANSHEE 0x0003121a +#define PCI_DEVICE_3DFX_VOODOO3 0x0005121a + +#define PCI_VENDOR_ID_FREEBSD 0x0 +#define PCI_DEVICE_ID_FREEBSD 0x2 +#define PCI_COMMAND_FREEBSD 0x4 +#define PCI_REVISION_ID_FREEBSD 0x8 +#define PCI_BASE_ADDRESS_0_FREEBSD 0x10 +#define SST1_PCI_SPECIAL1_FREEBSD 0x40 +#define SST1_PCI_SPECIAL2_FREEBSD 0x44 +#define SST1_PCI_SPECIAL3_FREEBSD 0x48 +#define SST1_PCI_SPECIAL4_FREEBSD 0x54 + +#define VGA_INPUT_STATUS_1C 0x3DA +#define VGA_MISC_OUTPUT_READ 0x3cc +#define VGA_MISC_OUTPUT_WRITE 0x3c2 +#define SC_INDEX 0x3c4 +#define SC_DATA 0x3c5 + +#define PCI_MAP_REG_START 0x10 +#define UNIT(m) (m & 0xf) + +/* IOCTL Calls */ +#define TDFX_IOC_TYPE_PIO 0 +#define TDFX_IOC_TYPE_QUERY '3' +#define TDFX_IOC_QRY_BOARDS 2 +#define TDFX_IOC_QRY_FETCH 3 +#define TDFX_IOC_QRY_UPDATE 4 +#include +#include +#include +#include + +struct tdfx_softc { + int cardno; + vm_offset_t addr; + struct resource *memrange, *piorange; + int memrid, piorid; + long range; + int vendor; + int type; + int addr0; + unsigned char bus; + unsigned char dv; + struct file *curFile; + device_t dev; + struct mem_range_desc mrdesc; + int busy; +}; + +struct tdfx_pio_data { + short port; + short size; + int device; + void *value; +}; + +#endif /* TDFX_VARS_H */ -- cgit v1.1