//------------------------------------------------------------------- // vram.c // // This character-mode Linux device-driver lets an application // program read from (or write to) the computer system's video // display memory -- bypassing any software windowing systems. // // It uses the kernel's PCI bus interface routines to identify // the graphics adapter, and to determine the physical address // and size of its on-board device memory (i.e., the so-called // graphics 'frame buffer'). It uses the kernel's 'ioremap()' // and 'iounmap()' functions to map pages of display memory to // virtual addresses in kernel-space, on an "as needed" basis. // It is analogus to the 'ram.c' driver that we wrote to allow // applications to have "read-only" access to ordinary memory. // // In this driver the device major number is hard-coded as 99, // and it is left for the user to create the device node using // the 'mknod' and 'chmod' shell commands: // // root# mknod /dev/vram c 99 0 // root# chmod a+rw /dev/vram // // NOTE: Written and tested using Linux kernel version 2.4.20. // // programmer: ALLAN CRUSE // written on: 09 APR 2003 //------------------------------------------------------------------- #define __KERNEL__ #define MODULE #include // for init_module() #include // for pci_find_class() #define VGA_CLASS 0x030000 // for VGA Display Adapters static char modname[] = "vram"; static char devname[] = "/dev/vram"; static int my_major = 99; // driver's assigned major-number static unsigned long fb_base; // physical address of frame-buffer static unsigned long fb_size; // size of the frame-buffer (bytes) static ssize_t my_write( struct file *file, const char *buf, size_t count, loff_t *pos ), my_read( struct file *file, char *buf, size_t count, loff_t *pos ); static loff_t my_llseek( struct file *file, loff_t offset, int whence ); static struct file_operations my_fops = { owner: THIS_MODULE, write: my_write, read: my_read, llseek: my_llseek, }; int init_module( void ) { struct pci_dev *devp = NULL; u32 save, temp; char *fb, *fw; int i; printk( "<1>\nInstalling \'%s\' module ", modname ); printk( "(major=%d) \n", my_major ); // locate the kernel's PCI device structure (for a VGA device) if ( devp = pci_find_class( VGA_CLASS, devp ) ) printk( "<1> VGA device: %s \n", devp->name ); else return -ENODEV; // determine the base-address for the video frame-buffer fb_base = pci_resource_start( devp, 0 ); printk( "<1> frame-buffer starts at address %08X \n", fb_base ); // determine the adapter's maximum supported memory-size temp = ~0; // set all bits of temp to 1 pci_read_config_dword( devp, 0x10, &save ); pci_write_config_dword( devp, 0x10, temp ); pci_read_config_dword( devp, 0x10, &temp ); pci_write_config_dword( devp, 0x10, save ); for (i = 4; i < 32; i++) if ( temp & (1< maximimum vram is %d MB ", fb_size >> 20 ); // check for possible memory-wrap fb = ioremap_nocache( fb_base, PAGE_SIZE ); while ( fb_size > PAGE_SIZE ) { char pel1, pel2; int sz = fb_size >> 1; fw = ioremap_nocache( fb_base + sz, PAGE_SIZE ); pel1 = fw[0]; fb[0] ^= ~0; pel2 = fw[0]; fb[0] ^= ~0; iounmap( fw ); if ( pel1 == pel2 ) break; fb_size = sz; } iounmap( fb ); printk( "(%d MB detected) \n", fb_size >> 20 ); // register our dervice-driver with the kernel return register_chrdev( my_major, modname, &my_fops ); } void cleanup_module( void ) { unregister_chrdev( my_major, modname ); printk( "<1>Removing \'%s\' module\n", modname ); } static loff_t my_llseek( struct file *file, loff_t offset, int whence ) { loff_t newpos; switch( whence ) { case 0: // SEEK_SET newpos = offset; break; case 1: // SEEK_CUR newpos = file->f_pos + offset; break; case 2: // SEEK_END newpos = fb_size + offset; break; default: // should never happen return -EINVAL; } if ( newpos < 0 ) return -EINVAL; file->f_pos = newpos; return newpos; } static ssize_t my_write( struct file *file, const char *buf, size_t count, loff_t *pos ) { unsigned long physaddr, page_number, page_offset, where; char *vaddr; // sanity check: we cannot write past end-of-file if ( *pos >= fb_size ) return -EINVAL; physaddr = fb_base + *pos; // where to start writing to // compute the memory-mapping parameters page_number = physaddr / PAGE_SIZE; // physical page-number page_offset = physaddr % PAGE_SIZE; // offset within page // sanity check: we cannot write past end-of-page if ( page_offset + count > PAGE_SIZE ) count = PAGE_SIZE - page_offset; // map physical page of vram to virtual address where = page_number * PAGE_SIZE; // address of the page vaddr = ioremap_nocache( where, PAGE_SIZE ); // setup mapping // copy count bytes to video memory from the caller's buffer memcpy_toio( vaddr + page_offset, buf, count ); // do writing iounmap( vaddr ); // unmap the page of video memory // notify kernel how many bytes of data were successfully written *pos += count; return count; } static ssize_t my_read( struct file *file, char *buf, size_t count, loff_t *pos ) { unsigned long physaddr, page_number, page_offset, where; char *vaddr; // sanity check: we cannot read past end-of-file if ( *pos >= fb_size ) return 0; physaddr = fb_base + *pos; // where to start reading from // compute the memory-mapping parameters page_number = physaddr / PAGE_SIZE; // physical page-number page_offset = physaddr % PAGE_SIZE; // offset within page // sanity check: we cannot read past end-of-page if ( page_offset + count > PAGE_SIZE ) count = PAGE_SIZE - page_offset; // map physical page of vram to virtual address where = page_number * PAGE_SIZE; // address of the page vaddr = ioremap_nocache( where, PAGE_SIZE ); // setup mapping // copy count bytes from video memory to the caller's buffer memcpy_fromio( buf, vaddr + page_offset, count ); // do reading iounmap( vaddr ); // unmap the page of video memory // notify kernel how many bytes of data were successfully read *pos += count; return count; } MODULE_LICENSE("GPL");