//------------------------------------------------------------------- // receiver.c (An initial character-driver prototype) // // This RealTek 8139 driver programs the network controller to // generate an interrupt whenever a network packet is received // in order to awaken a process that was waiting for new data. // // NOTE: Written and tested using Linux kernel version 2.6.10. // // programmer: ALLAN CRUSE // written on: 13 APR 2005 //------------------------------------------------------------------- #include // for init_module() #include // for init_waitqueue_head() #include // for pci_find_device() #include // for request_irq(), free_irq() #include // for copy_to_user() #define VENDOR_ID 0x10EC // ReakTek Semiconductors Corp #define DEVICE_ID 0x8139 // RTL-8139 Network Controller #define KBUFSIZ (8<<10) // Size of Kernel Buffer (8KB) #define RXCONFIG 0xE70F // Receive Configuration value char modname[] = "receiver"; char devname[] = "nic"; int my_major = 98; struct pci_dev *devp = NULL; int iobase, irq; void *kmem; unsigned long kmem_base; unsigned long kmem_size; wait_queue_head_t wq; irqreturn_t my_isr( int irq, void *dev_id, struct pt_regs *regs ) { static int rep = 0; int intstatus = inw( iobase + 0x3E ); if ( intstatus == 0 ) return IRQ_NONE; // it wasn't for us ++rep; // count the interrupt // printk( "#%d: intstatus=%04X \n", rep, intstatus ); if ( intstatus & 0x3 ) { wake_up_interruptible( &wq ); // wake up any readers outw( 0x3, iobase + 0x3E ); // clear the interrupt return IRQ_HANDLED; } printk( "unexpected RTL-8139 interrupt! " ); printk( " (intstatus=%04X) \n", intstatus ); outw( intstatus, iobase + 0x3E ); return IRQ_HANDLED; } ssize_t my_read( struct file *file, char *buf, size_t len, loff_t *pos ) { unsigned int offset, nbytes, header, status, length, nextrx; char *where = (char*)kmem; // sleep until the Receive Buffer is no longer empty wait_event_interruptible( wq, (inb( iobase + 0x37 )&1) == 0 ); // prepare to copy received packet, and to update CAPR offset = ( inw( iobase + 0x38 ) + 16 ) % KBUFSIZ; where += offset; header = *(unsigned int*)where; status = header & 0xFFFF; length = header >> 16; nextrx = offset + 4 + ((length + 3 )&~3); nextrx -= 16; nextrx %= KBUFSIZ; where += 4; // skip packet's header nbytes = length - 4; // omit the 4-bytes CRC if ( nbytes > len ) nbytes = len; // don't exceed request if ( copy_to_user( buf, where, nbytes ) ) return -EFAULT; // update the Receive Buffer's 'guard' register outw( nextrx, iobase + 0x38 ); // update CAPR register // advance the file position and report count of bytes copied *pos += nbytes; return nbytes; } int my_open( struct inode *inode, struct file *file ) { outb( 0x08, iobase + 0x37 ); // enable packet reception outl( RXCONFIG, iobase + 0x44 ); // setup Rx Configuration outw( KBUFSIZ-16, iobase + 0x38 ); // setup CAPR for KBUFSIZ outw( 0xFFFF, iobase + 0x3E ); // acknowledge interrupts outw( 0x0073, iobase + 0x3C ); // unmask recv interrupts return 0; // SUCCESS } int my_release( struct inode *inode, struct file *file ) { outb( 0x00, iobase + 0x37 ); // disable packet reception return 0; // SUCCESS } struct file_operations my_fops = { owner: THIS_MODULE, read: my_read, open: my_open, release: my_release, }; int init_module( void ) { printk( "<1>\nInstalling \'%s\' module ", modname ); printk( "(major=%d) \n", my_major ); devp = pci_find_device( VENDOR_ID, DEVICE_ID, devp ); if ( !devp ) return -ENODEV; irq = devp->irq; iobase = pci_resource_start( devp, 0 ); outb( 0x10, iobase + 0x37 ); // reset the network controller printk( "RealTek 8139 Network Interface: iobase=%04X \n", iobase ); while ( inb( iobase + 0x37 ) & 0x10 ); init_waitqueue_head( &wq ); if ( request_irq( irq, my_isr, SA_SHIRQ, devname, &devname ) ) return -EBUSY; kmem = kmalloc( KBUFSIZ, GFP_KERNEL ); if ( !kmem ) return -ENOMEM; kmem_base = __pa( kmem ); kmem_size = KBUFSIZ; printk( "Physical address-range of kernel buffer: " ); printk( "%08lX-%08lX \n", kmem_base, kmem_base+kmem_size ); outl( kmem_base, iobase + 0x30 ); // setup RBStart register return register_chrdev( my_major, devname, &my_fops ); } void cleanup_module( void ) { outw( 0x0000, iobase + 0x3C ); // disable device's interrupts outw( 0xFFFF, iobase + 0x0E ); // and ackowledge pending ones outb( 0x10, iobase + 0x37 ); // reset the network controller free_irq( irq, &devname ); // remove the interrupt handler unregister_chrdev( my_major, devname ); kfree( kmem ); printk( "<1>Removing \'%s\' module\n", modname ); } MODULE_LICENSE("GPL");