//------------------------------------------------------------------- // nictimer.c // // This is a limited-purpose character-mode device-driver for // the RealTek 8139 Network Controller. It is only concerned // with the Timer Count and the Timer Interrupt capabilities. // The driver's 'open()' function initializes the Timer Count // to begin counting from zero, and enables a timer interrupt // to be generated when this count reaches one million. (The // counter is restarted by the interrupt-handler, which keeps // track of the total number of interruptions.) The driver's // 'close()' function disables any subsequent interrupts, and // outputs a summarizing message to the screen (or log-file). // // NOTE: Written and tested with Linux kernel version 2.6.10. // // programmer: ALLAN CRUSE // date begun: 02 MAY 2005 // revised on: 04 MAY 2005 -- clear interrupts in 'my_open()' //------------------------------------------------------------------- #include // for init_module() #include // for pci_find_device #include // for register_chrdev() #include // for request_irq() #define VENDOR_ID 0x10EC // RealTek Semiconductors Corp #define DEVICE_ID 0x8139 // RLTK8139 Network Controller #define EXPIRES 1000000 // one million cycles char modname[] = "nictimer"; char devname[] = "nic"; int my_major = 98; int iobase, irq; int timer_count; unsigned long jiffie_save; irqreturn_t my_isr( int irq, void *dev_id, struct pt_regs *regs ) { int intstatus = inw( iobase + 0x3E ); if ( intstatus == 0 ) return IRQ_NONE; if ( intstatus & (1<<14) ) // timer-interrupt { ++timer_count; // increment our variable outl( 0, iobase + 0x48 ); // restart timer-counter printk( "timer interrupt! (intstatus=%04X)\n", intstatus ); } outw( intstatus, iobase + 0x3E ); // clears Interrupt Status return IRQ_HANDLED; } int my_open( struct inode *inode, struct file *file ) { timer_count = 0; // initialize our variable jiffie_save = jiffies; // remember current jiffies outl( 0, iobase + 0x48 ); // restart timer's counter outl( EXPIRES, iobase + 0x54 ); // set timer's expiration outw( 0xFFFF, iobase + 0x3E ); // clear pending interrupts outw( (1<<14), iobase + 0x3C ); // enable timer interrupt return 0; // SUCCESS } int my_close( struct inode *inode, struct file *file ) { jiffie_save = jiffies - jiffie_save; // compute elapsed jiffies outw( 0x0000, iobase + 0x3C ); // disable all interrupts outw( 0xFFFF, iobase + 0x3E ); // clear pending interrupts outl( 0, iobase + 0x54 ); // clear timer's expiration printk( "timer_count=%u jiffies=%lu \n", timer_count, jiffie_save ); return 0; // SUCCESS } struct file_operations my_fops = { owner: THIS_MODULE, open: my_open, release: my_close, }; int init_module( void ) { struct pci_dev *devp = NULL; printk( "<1>\nInstalling \'%s\' module\n", modname ); devp = pci_find_device( VENDOR_ID, DEVICE_ID, devp ); if ( !devp ) return -ENODEV; iobase = pci_resource_start( devp, 0 ); irq = devp->irq; outb( (1<<4), iobase + 0x37 ); // reset the network controller while ( inb( iobase + 0x37 )&(1<<4) ); // spin till reset done if ( request_irq( irq, my_isr, SA_SHIRQ, devname, &devname ) ) return -EBUSY; return register_chrdev( my_major, devname, &my_fops ); } void cleanup_module( void ) { free_irq( irq, &devname ); unregister_chrdev( my_major, devname ); printk( "<1>Removing \'%s\' module\n", modname ); } MODULE_LICENSE("GPL");