//------------------------------------------------------------------- // mmap8139.c // // This module implements a character device-driver that lets // an application map the RTL-8139 registers into user-space. // // NOTE: Written and tested with Linux kernel version 2.6.10. // // programmer: ALLAN CRUSE // written on: 04 APR 2005 // revised on: 24 OCT 2006 -- as Vladimir Neyelov suggested //------------------------------------------------------------------- #include // for init_module() #include // for pci_find_device() #define VENDOR_ID 0x10EC // RealTek Semiconductor Corp #define DEVICE_ID 0x8139 // RTL-8139 Network Processor char modname[] = "mmap8139"; // for displaying module name char devname[] = "nic"; // for registering the driver int my_major = 98; // static major-ID assignment unsigned long mmio_base; // registers physical address unsigned long mmio_size; // size of the registers bank int my_mmap( struct file *file, struct vm_area_struct *vma ) { unsigned long region_origin = vma->vm_pgoff * PAGE_SIZE; unsigned long region_length = vma->vm_end - vma->vm_start; unsigned long physical_addr = mmio_base + region_origin; unsigned long user_virtaddr = vma->vm_start; // determine page-offset -- Vladimir Neyelov's suggestion unsigned long phys_page_off = physical_addr & ~PAGE_MASK; // sanity check: mapped region is confined to just one page if ( region_length > PAGE_SIZE ) return -EINVAL; // tell the kernel not to try swapping out this region vma->vm_flags |= VM_RESERVED; // tell the kernel that this region maps io memory vma->vm_flags |= VM_IO; // ask the kernel to set up the required page-tables if ( io_remap_page_range( vma, user_virtaddr, physical_addr, region_length, vma->vm_page_prot ) ) return -EAGAIN; // add page offset to virtual page start -- Vladimir Neyelov vma->vm_start += phys_page_off; return 0; // SUCCESS } static struct file_operations my_fops = { owner: THIS_MODULE, mmap: my_mmap, }; int init_module( void ) { struct pci_dev *devp = NULL; printk( "<1>\nInstalling \'%s\' module\n", modname ); printk( " device-file is \'/dev/%s\' ", devname ); printk( "(major=%d) \n", my_major ); devp = pci_find_device( VENDOR_ID, DEVICE_ID, devp ); if ( !devp ) return -ENODEV; mmio_base = pci_resource_start( devp, 1 ); mmio_size = pci_resource_len( devp, 1 ); printk( " RealTek 8139 Network Interface:" ); printk( " %08lX-%08lX \n", mmio_base, mmio_base+mmio_size ); register_chrdev( my_major, devname, &my_fops ); return 0; // SUCCESS } void cleanup_module( void ) { unregister_chrdev( my_major, devname ); printk( "<1>Removing \'%s\' module\n", modname ); } MODULE_LICENSE("GPL"); //--------------------------------------------------------------------- // We owe thanks to Vladimir Neyelov for proposing the changes that are // incorporated into the 'my_mmap' function here, so that applications, // such as our 'nicregs.cpp' demo, which allow the kernel to select the // base-address for the requested memory-mapping, may nevertheless work // correctly in cases where the kernel's address is not 'page-aligned'. // -- Allan Cruse, 24 October 2006 //---------------------------------------------------------------------