/* * /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 #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 #else #include #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 #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 #else #define mod_inc_use_count #define mod_dec_use_count #endif #include #include #include #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,46) && \ LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) || \ defined(DEVFS_SUPPORT) #include #define HAVE_DEVFS 1 #else #define HAVE_DEVFS 0 #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) #include #include #define HAVE_CLASS_DEVICE 1 #else #define HAVE_CLASS_DEVICE 0 #endif #include #include #include #include #include #include #ifdef CONFIG_MTRR #include #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 HAVE_CLASS_DEVICE static struct class *class_3dfx = NULL; static struct device *device_3dfx = NULL; #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; numCards = 0; 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) { if (numCards >= MAXCARDS) { printk("3dfx: too many cards, MAXCARDS=%d\n", MAXCARDS); return -ENODEV; } 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) */ #if HAVE_CLASS_DEVICE static int create_device_3dfx(void) { class_3dfx = class_create(THIS_MODULE, "3dfx"); if (IS_ERR(class_3dfx)) { int ret = PTR_ERR(class_3dfx); class_3dfx = NULL; printk("3dfx: class_create() failed, returned %d\n", ret); return ret; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) device_3dfx = device_create(class_3dfx, NULL, MKDEV(MAJOR_3DFX, DEVICE_VOODOO), NULL, "3dfx"); #else device_3dfx = device_create(class_3dfx, NULL, MKDEV(MAJOR_3DFX, DEVICE_VOODOO), "3dfx"); #endif if (IS_ERR(device_3dfx)) { int ret = PTR_ERR(device_3dfx); device_3dfx = NULL; class_destroy(class_3dfx); class_3dfx = NULL; printk("3dfx: device_create() failed, returned %d\n", ret); return ret; } return 0; } static void destroy_device_3dfx(void) { if (device_3dfx) device_destroy(class_3dfx, MKDEV(MAJOR_3DFX, DEVICE_VOODOO)); if (class_3dfx) class_destroy(class_3dfx); device_3dfx = NULL; class_3dfx = NULL; } #endif #ifdef MODULE int init_module(void) { int ret; DEBUGMSG(("3dfx: Entering init_module()\n")); ret = findCards(); if (ret < 0) { printk("3dfx: findCards() failed, returned %d\n", ret); return -ENODEV; } if ((ret = register_chrdev(MAJOR_3DFX, "3dfx", &fops_3dfx)) < 0) { printk("3dfx: Unable to register character device with major %d\n", MAJOR_3DFX); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) pci_unregister_driver(&driver_3dfx); #endif 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 #if HAVE_CLASS_DEVICE ret = create_device_3dfx(); if (ret < 0) { unregister_chrdev(MAJOR_3DFX, "3dfx"); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) pci_unregister_driver(&driver_3dfx); #endif return ret; } #endif #ifdef CONFIG_MTRR ret = setmtrr_3dfx(); if (ret < 0) { DEBUGMSG(("setmtrr_3dfx() failed, returned %d\n", 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 #if HAVE_CLASS_DEVICE destroy_device_3dfx(); #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; ret = findCards(); if (ret < 0) { printk("3dfx: findCards() failed, returned %d\n", ret); return -ENODEV; } if (ret=register_chrdev(MAJOR_3DFX, "3dfx", &fops_3dfx)) { DEBUGMSG(("3dfx: Unable to register_chrdev with major %d\n", MAJOR_3DFX)); return ret; } 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("2026.03.24"); #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 #endif