950 lines
21 KiB
C
950 lines
21 KiB
C
/*
|
|
* /dev/3dfx device for Linux kernels with MTRR settings enabled.
|
|
*
|
|
* Original by Daryll Straus.
|
|
* Port to 2.1 kernel by Jon Taylor.
|
|
* setmtrr_3dfx() added by Jens Axboe.
|
|
* Combining 2.0 and 2.1/2.2 kernels into one rpm,
|
|
* resetmtrr_3df(), and using correct pci calls for
|
|
* 2.1/2.2 kernels by Carlo Wood.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Include this first as it defines things that affect the kernel headers.
|
|
*/
|
|
#include <linux/version.h>
|
|
|
|
#ifndef KERNEL_VERSION
|
|
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
|
|
#endif
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33)
|
|
#include <generated/autoconf.h>
|
|
#else
|
|
#include <linux/autoconf.h>
|
|
#endif
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,115)
|
|
/*
|
|
* It might work with smaller kernels, but I never tested that.
|
|
*/
|
|
#error "Upgrade your kernel"
|
|
#endif
|
|
|
|
#ifdef MODULE
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
|
|
#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS)
|
|
#define MODVERSIONS
|
|
#endif
|
|
#ifdef MODVERSIONS
|
|
#include <linux/modversions.h>
|
|
#endif
|
|
#define mod_inc_use_count MOD_INC_USE_COUNT
|
|
#define mod_dec_use_count MOD_DEC_USE_COUNT
|
|
#else
|
|
#define mod_inc_use_count
|
|
#define mod_dec_use_count
|
|
#endif
|
|
#include <linux/module.h>
|
|
#else
|
|
#define mod_inc_use_count
|
|
#define mod_dec_use_count
|
|
#endif
|
|
|
|
#include <linux/types.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/pci.h>
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,46) && \
|
|
LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) || \
|
|
defined(DEVFS_SUPPORT)
|
|
#include <linux/devfs_fs_kernel.h>
|
|
#define HAVE_DEVFS 1
|
|
#else
|
|
#define HAVE_DEVFS 0
|
|
#endif
|
|
#include <asm/segment.h>
|
|
#include <asm/ioctl.h>
|
|
#include <asm/io.h>
|
|
#include <asm/pgtable.h>
|
|
#include <asm/processor.h>
|
|
|
|
#include <asm/uaccess.h>
|
|
#ifdef CONFIG_MTRR
|
|
#include <asm/mtrr.h>
|
|
#endif
|
|
|
|
#define MAJOR_3DFX 107
|
|
#define DEVICE_VOODOO 0
|
|
|
|
#define PCI_VENDOR_ID_LINUX 0x0
|
|
#define PCI_DEVICE_ID_LINUX 0x2
|
|
#define PCI_COMMAND_LINUX 0x4
|
|
#define PCI_REVISION_ID_LINUX 0x8
|
|
#define PCI_BASE_ADDRESS_0_LINUX 0x10
|
|
#define PCI_BASE_ADDRESS_1_LINUX 0x14
|
|
#define PCI_BASE_ADDRESS_2_LINUX 0x18
|
|
#define SST1_PCI_SPECIAL1_LINUX 0x40
|
|
#define SST1_PCI_SPECIAL2_LINUX 0x44
|
|
#define SST1_PCI_SPECIAL3_LINUX 0x48
|
|
#define SST1_PCI_SPECIAL4_LINUX 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
|
|
|
|
#ifndef PCI_VENDOR_ID_3DFX
|
|
#define PCI_VENDOR_ID_3DFX 0x121a
|
|
#endif
|
|
|
|
#ifndef PCI_VENDOR_ID_ALLIANCE
|
|
#define PCI_VENDOR_ID_ALLIANCE 0x1142
|
|
#endif
|
|
|
|
#ifndef PCI_VENDOR_ID_MACRONIX
|
|
#define PCI_VENDOR_ID_MACRONIX 0x10d9
|
|
#endif
|
|
|
|
#ifndef PCI_DEVICE_ID_3DFX_VOODOO2
|
|
#define PCI_DEVICE_ID_3DFX_VOODOO2 2
|
|
#endif
|
|
|
|
#ifndef PCI_DEVICE_ID_ALLIANCE_AT3D
|
|
#define PCI_DEVICE_ID_ALLIANCE_AT3D 0x643d
|
|
#endif
|
|
|
|
#ifndef PCI_DEVICE_ID_MACRONIX_MX86251
|
|
#define PCI_DEVICE_ID_MACRONIX_MX86251 0x8626
|
|
#endif
|
|
|
|
#ifndef PCI_DEVICE_ID_3DFX_BANSHEE
|
|
#define PCI_DEVICE_ID_3DFX_BANSHEE 3
|
|
#endif
|
|
|
|
#ifndef PCI_DEVICE_ID_3DFX_VOODOO3
|
|
#define PCI_DEVICE_ID_3DFX_VOODOO3 5
|
|
#endif
|
|
|
|
#ifndef PCI_DEVICE_ID_3DFX_VOODOO4
|
|
#define PCI_DEVICE_ID_3DFX_VOODOO4 9
|
|
#endif
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,73)
|
|
#define pci_get_device pci_find_device
|
|
#define pci_dev_put(dev)
|
|
#endif
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
|
|
static char name_3dfx[] = "3dfx";
|
|
|
|
static struct pci_device_id id_table_3dfx[] = {
|
|
{PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
|
{PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO2,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
|
{PCI_VENDOR_ID_ALLIANCE, PCI_DEVICE_ID_ALLIANCE_AT3D,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
|
{PCI_VENDOR_ID_MACRONIX, PCI_DEVICE_ID_MACRONIX_MX86251,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
|
{PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_BANSHEE,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
|
{PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO3,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
|
{PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO4,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
|
{ 0, }
|
|
};
|
|
#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) */
|
|
static struct pci_card {
|
|
unsigned short vendor;
|
|
unsigned short device;
|
|
} pci_card_list[] = {
|
|
{PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO},
|
|
{PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO2},
|
|
{PCI_VENDOR_ID_ALLIANCE, PCI_DEVICE_ID_ALLIANCE_AT3D},
|
|
{PCI_VENDOR_ID_MACRONIX, PCI_DEVICE_ID_MACRONIX_MX86251},
|
|
{PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_BANSHEE},
|
|
{PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO3},
|
|
{PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO4}
|
|
};
|
|
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) */
|
|
|
|
#ifdef DEBUG
|
|
#define DEBUGMSG(x) printk x
|
|
#else
|
|
#define DEBUGMSG(x)
|
|
#endif
|
|
|
|
/*
|
|
* This macro is for accessing vma->vm_offset or vma->vm_pgoff depending
|
|
* on kernel version.
|
|
*/
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,14)
|
|
#define VM_OFFSET(vma) (vma->vm_offset)
|
|
#define VM_OFFSET_ALIGNED(vma) ((vma->vm_offset) & ~PAGE_MASK)
|
|
#else
|
|
#define VM_OFFSET(vma) (vma->vm_pgoff << PAGE_SHIFT)
|
|
#define VM_OFFSET_ALIGNED(vma) ((vma->vm_pgoff) & ~PAGE_MASK)
|
|
#endif
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
|
|
#define my_remap_page_range(vma, start, ofs, len, prot) \
|
|
remap_pfn_range(vma, start, ofs >> PAGE_SHIFT, len, prot)
|
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
|
|
#define my_remap_page_range(vma, start, ofs, len, prot) \
|
|
remap_page_range(vma, start, ofs, len, prot)
|
|
#else
|
|
#define my_remap_page_range(vma, start, ofs, len, prot) \
|
|
remap_page_range(start, ofs, len, prot)
|
|
#endif
|
|
|
|
struct pioData_t {
|
|
short port;
|
|
short size;
|
|
int device;
|
|
void *value;
|
|
};
|
|
|
|
struct cardInfo_t {
|
|
int vendor;
|
|
int type;
|
|
int addr0;
|
|
int addr1;
|
|
int addr2;
|
|
unsigned char bus;
|
|
struct pci_dev *dev;
|
|
struct file *curFile;
|
|
#ifdef CONFIG_MTRR
|
|
int mtrr_buf;
|
|
int mtrr_ctl;
|
|
#endif
|
|
};
|
|
|
|
typedef struct pioData_t pioData;
|
|
typedef struct cardInfo_t cardInfo;
|
|
|
|
#ifdef MODULE
|
|
void cleanup_module(void);
|
|
#endif
|
|
|
|
#define MAXCARDS 16
|
|
|
|
static cardInfo cards[MAXCARDS];
|
|
static int numCards = 0;
|
|
#if HAVE_DEVFS
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,46) && \
|
|
LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) || \
|
|
defined(DEVFS_SUPPORT)
|
|
static devfs_handle_t devfs_handle;
|
|
#endif
|
|
#endif
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
|
|
static void findCardType(int vendor, int device)
|
|
{
|
|
struct pci_dev *dev = NULL;
|
|
|
|
while (numCards < MAXCARDS && (dev = pci_get_device(vendor, device, dev))) {
|
|
pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &cards[numCards].addr0);
|
|
pci_read_config_dword(dev, PCI_BASE_ADDRESS_1, &cards[numCards].addr1);
|
|
pci_read_config_dword(dev, PCI_BASE_ADDRESS_2, &cards[numCards].addr2);
|
|
cards[numCards].bus = dev->bus->number;
|
|
cards[numCards].dev = dev;
|
|
|
|
cards[numCards].addr0 &= ~0xF;
|
|
cards[numCards].addr1 &= ~0xF;
|
|
cards[numCards].vendor = vendor;
|
|
cards[numCards].type = device;
|
|
cards[numCards].curFile = 0;
|
|
|
|
DEBUGMSG(("3dfx: board vendor %d type %d located at %x/%x bus %d dev %d\n",
|
|
vendor, device,
|
|
cards[numCards].addr0, cards[numCards].addr1,
|
|
cards[numCards].bus, cards[numCards].dev->devfn));
|
|
|
|
++numCards;
|
|
}
|
|
}
|
|
|
|
static int findCards(void)
|
|
{
|
|
int i;
|
|
|
|
if (!pci_present())
|
|
return 0;
|
|
numCards = 0;
|
|
for (i = 0; i < (sizeof(pci_card_list)/sizeof(struct pci_card)); i++)
|
|
findCardType(pci_card_list[i].vendor, pci_card_list[i].device);
|
|
|
|
return numCards;
|
|
}
|
|
#else
|
|
static struct pci_driver driver_3dfx;
|
|
|
|
static int findCards(void)
|
|
{
|
|
int error;
|
|
|
|
error = pci_register_driver(&driver_3dfx);
|
|
|
|
if (error)
|
|
return error < 0 ? error : -error;
|
|
else
|
|
return numCards;
|
|
}
|
|
#endif
|
|
|
|
static int open_3dfx(struct inode *inode, struct file *file)
|
|
{
|
|
DEBUGMSG(("3dfx: Entering open_3dfx\n"));
|
|
|
|
if (!numCards) {
|
|
printk("3dfx: No 3Dfx cards found\n");
|
|
return -ENODEV;
|
|
}
|
|
mod_inc_use_count;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int release_3dfx(struct inode *inode, struct file *file)
|
|
{
|
|
int i;
|
|
|
|
DEBUGMSG(("3dfx: Entering release_3dfx\n"));
|
|
|
|
for (i = 0; i < numCards; ++i)
|
|
if (cards[i].curFile == file)
|
|
cards[i].curFile = 0;
|
|
mod_dec_use_count;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mmap_3dfx(struct file *file, struct vm_area_struct *vma)
|
|
{
|
|
size_t len;
|
|
int i;
|
|
|
|
DEBUGMSG(("3dfx: Entering mmap_3dfx\n"));
|
|
|
|
for (i = 0; i < numCards; ++i) {
|
|
if ((cards[i].addr0 == VM_OFFSET(vma)) ||
|
|
(cards[i].addr1 == VM_OFFSET(vma)))
|
|
break;
|
|
}
|
|
if (i == numCards) {
|
|
DEBUGMSG(("3dfx: Couldn't match address %lx to a card\n",
|
|
VM_OFFSET(vma)));
|
|
return -EPERM;
|
|
}
|
|
if (VM_OFFSET_ALIGNED(vma)) {
|
|
DEBUGMSG(("3dfx: Map request not page aligned\n"));
|
|
return -ENXIO;
|
|
}
|
|
len = vma->vm_end - vma->vm_start;
|
|
if ((len < 0) || (len > 0x2000000)) {
|
|
DEBUGMSG(("3dfx: Invalid mapping size requested\n"));
|
|
return -EINVAL;
|
|
}
|
|
#if defined(__i386__)
|
|
pgprot_val(vma->vm_page_prot) |= _PAGE_PCD;
|
|
#endif
|
|
if (my_remap_page_range(vma, vma->vm_start, VM_OFFSET(vma), len,
|
|
vma->vm_page_prot)) {
|
|
DEBUGMSG(("3dfx: Page remap failed\n"));
|
|
return -EAGAIN;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int doQueryBoards(void)
|
|
{
|
|
return numCards;
|
|
}
|
|
|
|
static int doQueryFetch(pioData *desc)
|
|
{
|
|
char retchar;
|
|
short retword;
|
|
int retlong;
|
|
int i;
|
|
|
|
if (desc->device < 0 || desc->device >= numCards)
|
|
return -EINVAL;
|
|
i = desc->device;
|
|
|
|
switch (desc->port) {
|
|
case PCI_VENDOR_ID_LINUX:
|
|
if (desc->size != 2)
|
|
return -EINVAL;
|
|
if (copy_to_user(desc->value, &cards[i].vendor, desc->size))
|
|
return -EFAULT;
|
|
return 0;
|
|
case PCI_DEVICE_ID_LINUX:
|
|
if (desc->size != 2)
|
|
return -EINVAL;
|
|
if (copy_to_user(desc->value, &cards[i].type, desc->size))
|
|
return -EFAULT;
|
|
return 0;
|
|
case PCI_BASE_ADDRESS_0_LINUX:
|
|
if (desc->size != 4)
|
|
return -EINVAL;
|
|
if (copy_to_user(desc->value, &cards[i].addr0, desc->size))
|
|
return -EFAULT;
|
|
return 0;
|
|
case PCI_BASE_ADDRESS_1_LINUX:
|
|
if (desc->size != 4)
|
|
return -EINVAL;
|
|
if (copy_to_user(desc->value, &cards[i].addr1, desc->size))
|
|
return -EFAULT;
|
|
return 0;
|
|
case PCI_BASE_ADDRESS_2_LINUX:
|
|
if (desc->size != 4)
|
|
return -EINVAL;
|
|
if (copy_to_user(desc->value, &cards[i].addr2, desc->size))
|
|
return -EFAULT;
|
|
return 0;
|
|
case SST1_PCI_SPECIAL1_LINUX:
|
|
if (desc->size != 4)
|
|
return -EINVAL;
|
|
break;
|
|
case PCI_REVISION_ID:
|
|
if (desc->size != 1)
|
|
return -EINVAL;
|
|
break;
|
|
case SST1_PCI_SPECIAL4_LINUX:
|
|
if (desc->size != 4)
|
|
return -EINVAL;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (desc->size) {
|
|
case 1:
|
|
pci_read_config_byte(cards[i].dev, desc->port, &retchar);
|
|
if (copy_to_user(desc->value, &retchar, 1))
|
|
return -EFAULT;
|
|
break;
|
|
case 2:
|
|
pci_read_config_word(cards[i].dev, desc->port, &retword);
|
|
if (copy_to_user(desc->value, &retword, 2))
|
|
return -EFAULT;
|
|
break;
|
|
case 4:
|
|
pci_read_config_dword(cards[i].dev, desc->port, &retlong);
|
|
if (copy_to_user(desc->value, &retlong, 4))
|
|
return -EFAULT;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int doQueryUpdate(pioData *desc)
|
|
{
|
|
int retval;
|
|
int preval;
|
|
int mask;
|
|
char retchar;
|
|
short retword;
|
|
int retlong;
|
|
|
|
if (desc->device < 0 || desc->device >= numCards)
|
|
return -EINVAL;
|
|
|
|
switch (desc->port) {
|
|
case PCI_COMMAND_LINUX:
|
|
if (desc->size != 2)
|
|
return -EINVAL;
|
|
break;
|
|
case SST1_PCI_SPECIAL1_LINUX:
|
|
if (desc->size != 4)
|
|
return -EINVAL;
|
|
break;
|
|
case SST1_PCI_SPECIAL2_LINUX:
|
|
if (desc->size != 4)
|
|
return -EINVAL;
|
|
break;
|
|
case SST1_PCI_SPECIAL3_LINUX:
|
|
if (desc->size != 4)
|
|
return -EINVAL;
|
|
break;
|
|
case SST1_PCI_SPECIAL4_LINUX:
|
|
if (desc->size != 4)
|
|
return -EINVAL;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
pci_read_config_dword(cards[desc->device].dev, desc->port & ~0x3, &retval);
|
|
switch (desc->size) {
|
|
case 1:
|
|
if (copy_from_user(&retchar, desc->value, 1))
|
|
return -EFAULT;
|
|
preval = retchar << (8 * (desc->port & 0x3));
|
|
mask = 0xFF << (8 * (desc->port & 0x3));
|
|
break;
|
|
case 2:
|
|
if (copy_from_user(&retword, desc->value, 2))
|
|
return -EFAULT;
|
|
preval = retword << (8 * (desc->port & 0x3));
|
|
mask = 0xFFFF << (8 * (desc->port & 0x3));
|
|
break;
|
|
case 4:
|
|
if (copy_from_user(&retlong, desc->value, 4))
|
|
return -EFAULT;
|
|
preval = retlong;
|
|
mask = ~0;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
retval = (retval & ~mask) | preval;
|
|
pci_write_config_dword(cards[desc->device].dev, desc->port, retval);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int doQuery(unsigned int cmd, unsigned long arg)
|
|
{
|
|
pioData desc;
|
|
|
|
if (_IOC_NR(cmd) == 2)
|
|
return doQueryBoards();
|
|
if (copy_from_user(&desc, (void *)arg, sizeof(pioData)))
|
|
return -EFAULT;
|
|
if (_IOC_NR(cmd) == 3)
|
|
return doQueryFetch(&desc);
|
|
if (_IOC_NR(cmd) == 4)
|
|
return doQueryUpdate(&desc);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int doPIORead(pioData *desc)
|
|
{
|
|
int retchar;
|
|
|
|
/* full SSTIO aperture is defined by:
|
|
* unsigned short port = desc->port;
|
|
* unsigned short base = cards[i].addr2 & ~1;
|
|
* (base <= port && port <= (base + 0x108 - desc->size))
|
|
*/
|
|
switch (desc->port) {
|
|
case VGA_INPUT_STATUS_1C:
|
|
break;
|
|
case SC_INDEX:
|
|
break;
|
|
case SC_DATA:
|
|
break;
|
|
case VGA_MISC_OUTPUT_READ:
|
|
break;
|
|
default:
|
|
return -EPERM;
|
|
}
|
|
|
|
switch (desc->size) {
|
|
case 1:
|
|
retchar = inb(desc->port);
|
|
break;
|
|
case 2:
|
|
retchar = inw(desc->port);
|
|
break;
|
|
case 4:
|
|
retchar = inl(desc->port);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (copy_to_user(desc->value, &retchar, desc->size))
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int doPIOWrite(pioData *desc)
|
|
{
|
|
int retchar;
|
|
|
|
/* full SSTIO aperture is defined by:
|
|
* unsigned short port = desc->port;
|
|
* unsigned short base = cards[i].addr2 & ~1;
|
|
* (base <= port && port <= (base + 0x108 - desc->size))
|
|
*/
|
|
switch (desc->port) {
|
|
case SC_INDEX:
|
|
break;
|
|
case SC_DATA:
|
|
break;
|
|
case VGA_MISC_OUTPUT_WRITE:
|
|
break;
|
|
default:
|
|
return -EPERM;
|
|
}
|
|
|
|
if (copy_from_user(&retchar, desc->value, desc->size))
|
|
return -EFAULT;
|
|
|
|
switch (desc->size) {
|
|
case 1:
|
|
outb(retchar, desc->port);
|
|
break;
|
|
case 2:
|
|
outw(retchar, desc->port);
|
|
break;
|
|
case 4:
|
|
outl(retchar, desc->port);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int doPIO(unsigned int cmd, unsigned long arg)
|
|
{
|
|
pioData desc;
|
|
|
|
if (copy_from_user(&desc, (void *)arg, sizeof(pioData)))
|
|
return -EFAULT;
|
|
if (_IOC_DIR(cmd) == _IOC_READ)
|
|
return doPIORead(&desc);
|
|
if (_IOC_DIR(cmd) == _IOC_WRITE)
|
|
return doPIOWrite(&desc);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
#ifdef HAVE_UNLOCKED_IOCTL
|
|
static long ioctl_3dfx(struct file *file, unsigned int cmd, unsigned long arg)
|
|
#else
|
|
static int ioctl_3dfx(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
|
|
#endif
|
|
{
|
|
#ifdef HAVE_UNLOCKED_IOCTL
|
|
DEBUGMSG(("3dfx: Entering ioctl_3dfx, file %p cmd %x arg %lx\n",
|
|
file, cmd, arg));
|
|
#else
|
|
DEBUGMSG(("3dfx: Entering ioctl_3dfx, inode %p file %p cmd %x arg %lx\n", inode, file, cmd, arg));
|
|
#endif
|
|
|
|
switch (_IOC_TYPE(cmd)) {
|
|
case '3':
|
|
return doQuery(cmd, arg);
|
|
case 0:
|
|
return doPIO(cmd, arg);
|
|
default:
|
|
DEBUGMSG(("3dfx: Unknown 3dfx request made\n"));
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_MTRR
|
|
static int setmtrr_3dfx(void)
|
|
{
|
|
int i = 0, retval = -2;
|
|
unsigned char dlc;
|
|
|
|
/*
|
|
* First do a bios fixup if this system has a 82441FX chipset.
|
|
*/
|
|
struct pci_dev *dev = NULL;
|
|
dev = pci_get_device(PCI_VENDOR_ID_INTEL,
|
|
PCI_DEVICE_ID_INTEL_82371SB_0, dev);
|
|
if (dev) {
|
|
pci_read_config_byte(dev, 0x82, &dlc);
|
|
if (!(dlc & 1 << 1)) {
|
|
dlc |= 1 << 1;
|
|
pci_write_config_byte(dev, 0x82, dlc);
|
|
printk("3dfx: PIIX3: Enabling Passive Release\n");
|
|
}
|
|
pci_dev_put(dev);
|
|
}
|
|
|
|
/*
|
|
* Set up the mtrr's.
|
|
*/
|
|
if (numCards == 0)
|
|
return -EIO;
|
|
for (i = 0; i < numCards; i++) {
|
|
if ((cards[i].vendor != PCI_VENDOR_ID_3DFX) ||
|
|
(cards[i].type > PCI_DEVICE_ID_3DFX_VOODOO3)) {
|
|
/*
|
|
* Used as flag in resetmtrr_3dfx().
|
|
*/
|
|
cards[i].mtrr_buf = -1;
|
|
continue;
|
|
}
|
|
switch (cards[i].type) {
|
|
case PCI_DEVICE_ID_3DFX_VOODOO:
|
|
case PCI_DEVICE_ID_3DFX_VOODOO2:
|
|
/*
|
|
* Frame buffer to write combining.
|
|
*/
|
|
retval = cards[i].mtrr_buf = mtrr_add(cards[i].addr0, 0x400000, MTRR_TYPE_WRCOMB, 1);
|
|
if (retval >= 0) {
|
|
retval = cards[i].mtrr_ctl = mtrr_add(cards[i].addr0, 0x1000, MTRR_TYPE_UNCACHABLE, 1);
|
|
if (retval < 0) {
|
|
mtrr_del(cards[i].mtrr_buf, 0, 0);
|
|
cards[i].mtrr_buf = -1;
|
|
}
|
|
}
|
|
if (retval < 0) {
|
|
printk("3dfx: Could not set MTRR for Voodoo card\n");
|
|
/*
|
|
* Can still run.
|
|
*/
|
|
return 0;
|
|
}
|
|
break;
|
|
case PCI_DEVICE_ID_3DFX_BANSHEE:
|
|
case PCI_DEVICE_ID_3DFX_VOODOO3:
|
|
retval = cards[i].mtrr_buf = mtrr_add(cards[i].addr1, 0x1000000, MTRR_TYPE_WRCOMB, 1);
|
|
if (retval < 0) {
|
|
printk("3dfx: Could not set MTRR for Voodoo card\n");
|
|
/*
|
|
* Can still run
|
|
*/
|
|
return 0;
|
|
}
|
|
cards[i].mtrr_ctl = -1;
|
|
break;
|
|
default:
|
|
/*
|
|
* We should never hit this
|
|
*/
|
|
break;
|
|
}
|
|
}
|
|
if (retval == -2) {
|
|
DEBUGMSG(("3dfx: Could not set MTRR for this graphics card\n"));
|
|
/*
|
|
* Can still run
|
|
*/
|
|
retval = 0;
|
|
}
|
|
#ifdef DEBUG
|
|
else if (retval >= 0)
|
|
DEBUGMSG(("3dfx: Successfully set MTRR, mtrr register: %d\n",
|
|
retval));
|
|
#endif
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int resetmtrr_3dfx(void)
|
|
{
|
|
int i, ret, retval = 0;
|
|
|
|
for (i = 0; i < numCards; i++) {
|
|
if (cards[i].mtrr_buf >= 0) {
|
|
ret = mtrr_del(cards[i].mtrr_buf, 0, 0);
|
|
if (ret < 0)
|
|
retval = ret;
|
|
cards[i].mtrr_buf = -1;
|
|
if (cards[i].mtrr_ctl >= 0) {
|
|
ret = mtrr_del(cards[i].mtrr_ctl, 0, 0);
|
|
if (ret < 0)
|
|
retval = ret;
|
|
cards[i].mtrr_ctl = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
#endif /* CONFIG_MTRR */
|
|
|
|
static struct file_operations fops_3dfx = {
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
|
|
.owner = THIS_MODULE,
|
|
#endif
|
|
#ifdef HAVE_UNLOCKED_IOCTL
|
|
.unlocked_ioctl = ioctl_3dfx,
|
|
#else
|
|
.ioctl = ioctl_3dfx,
|
|
#endif
|
|
.mmap = mmap_3dfx,
|
|
.open = open_3dfx,
|
|
.release = release_3dfx,
|
|
};
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
|
|
static int probe_3dfx(struct pci_dev *dev, const struct pci_device_id *id)
|
|
{
|
|
pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &cards[numCards].addr0);
|
|
pci_read_config_dword(dev, PCI_BASE_ADDRESS_1, &cards[numCards].addr1);
|
|
pci_read_config_dword(dev, PCI_BASE_ADDRESS_2, &cards[numCards].addr2);
|
|
cards[numCards].addr0 &= ~0xF;
|
|
cards[numCards].addr1 &= ~0xF;
|
|
cards[numCards].dev = dev;
|
|
cards[numCards].vendor = dev->vendor;
|
|
cards[numCards].type = dev->device;
|
|
cards[numCards].curFile = 0;
|
|
|
|
|
|
DEBUGMSG(("3dfx: board vendor %d type %d located at %x/%x\n",
|
|
dev->vendor, dev->device, cards[numCards].addr0,
|
|
cards[numCards].addr1));
|
|
numCards++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void remove_3dfx(struct pci_dev *dev)
|
|
{
|
|
|
|
}
|
|
|
|
MODULE_DEVICE_TABLE(pci, id_table_3dfx);
|
|
|
|
static struct pci_driver driver_3dfx = {
|
|
.name = name_3dfx,
|
|
.id_table = id_table_3dfx,
|
|
.probe = probe_3dfx,
|
|
.remove = remove_3dfx,
|
|
};
|
|
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) */
|
|
|
|
#ifdef MODULE
|
|
int init_module(void)
|
|
{
|
|
int ret;
|
|
|
|
DEBUGMSG(("3dfx: Entering init_module()\n"));
|
|
|
|
if ((ret = register_chrdev(MAJOR_3DFX, "3dfx", &fops_3dfx)) < 0) {
|
|
printk("3dfx: Unable to register character device with major %d\n", MAJOR_3DFX);
|
|
return ret;
|
|
}
|
|
|
|
#if HAVE_DEVFS
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
|
|
devfs_mk_cdev(MKDEV(MAJOR_3DFX, DEVICE_VOODOO),
|
|
S_IFCHR | S_IROTH | S_IWOTH | S_IRGRP | S_IWGRP,
|
|
"3dfx");
|
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,46) || defined(DEVFS_SUPPORT)
|
|
devfs_handle = devfs_register(NULL, "3dfx", DEVFS_FL_NONE,
|
|
MAJOR_3DFX, DEVICE_VOODOO,
|
|
S_IFCHR | S_IROTH | S_IWOTH | S_IRGRP | S_IWGRP,
|
|
&fops_3dfx, NULL);
|
|
#endif
|
|
#endif
|
|
|
|
DEBUGMSG(("3dfx: Successfully registered device 3dfx\n"));
|
|
ret = findCards();
|
|
if (ret < 0) {
|
|
printk("3dfx: findCards() failed, returned %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CONFIG_MTRR
|
|
ret = setmtrr_3dfx();
|
|
if (ret < 0) {
|
|
DEBUGMSG(("setmtrr_3dfx() failed, returned %d\n", ret));
|
|
/*
|
|
unregister_chrdev(MAJOR_3DFX, "3dfx");
|
|
return ret;
|
|
*/
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
void cleanup_module(void)
|
|
{
|
|
DEBUGMSG(("3dfx: Entering cleanup_module\n"));
|
|
|
|
#ifdef CONFIG_MTRR
|
|
resetmtrr_3dfx();
|
|
#endif
|
|
#if HAVE_DEVFS
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
|
|
devfs_remove("3dfx");
|
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,46) || defined(DEVFS_SUPPORT)
|
|
devfs_unregister(devfs_handle);
|
|
#endif
|
|
#endif
|
|
|
|
unregister_chrdev(MAJOR_3DFX, "3dfx");
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
|
|
pci_unregister_driver(&driver_3dfx);
|
|
#endif
|
|
}
|
|
#else /* !MODULE */
|
|
|
|
long init_3dfx(long mem_start, long mem_end)
|
|
{
|
|
int ret;
|
|
|
|
if (register_chrdev(MAJOR_3DFX, "3dfx", &fops_3dfx)) {
|
|
DEBUGMSG(("3dfx: Unable to register_chrdev with major %d\n", MAJOR_3DFX));
|
|
return 0;
|
|
}
|
|
|
|
ret = findCards();
|
|
if (ret < 0) {
|
|
printk("3dfx: findCards() failed, returned %d\n", ret);
|
|
return 0;
|
|
}
|
|
|
|
return mem_start;
|
|
}
|
|
#endif /* !MODULE */
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,21)
|
|
MODULE_AUTHOR("Daryll Strauss et al.");
|
|
MODULE_DESCRIPTION("3dfx glide kernel device driver");
|
|
MODULE_LICENSE("GPL");
|
|
#ifdef MODULE_VERSION
|
|
MODULE_VERSION("2011.05.27");
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(DEBUG)
|
|
/*
|
|
* Kludge to get rid of:
|
|
* ./3dfx.o: unresolved symbol inb
|
|
* ./3dfx.o: unresolved symbol inl
|
|
* ./3dfx.o: unresolved symbol inw
|
|
* ./3dfx.o: unresolved symbol outb
|
|
* ./3dfx.o: unresolved symbol outl
|
|
* ./3dfx.o: unresolved symbol outw
|
|
* when compiling this module with -g during debugging.
|
|
*/
|
|
#undef __KERNEL__
|
|
#define extern
|
|
#undef _ASM_IO_H
|
|
#include <asm/io.h>
|
|
#endif
|
|
|