//------------------------------------------------------------------- // trypause.c // // This module demonstrates use of the SWXOFF-bit (bit 22) in // the NIC's Transmit Control register to offload the sending // of Flow Control 'pause' frames under software control, but // it uses a non-standard Ether Type in order that the Switch // connected to our 'anchor' machines will pass these packets // on to another station where we can see them with 'nicspy'. // // NOTE: Written and tested on Linux kernel version 2.6.22.5. // // programmer: ALLAN CRUSE // written on: 12 MAY 2008 //------------------------------------------------------------------- #include // for init_module() #include // for create_proc_info_entry() #include // for pci_get_device() #define VENDOR_ID 0x8086 // Intel Corporation #define DEVICE_ID 0x109A // 82573L controller //#define DEVICE_ID 0x10B9 // 82572EI controller char modname[] = "trypause"; struct pci_dev *devp; unsigned int mmio_base; unsigned int mmio_size; void *io; enum { E1000_CTRL = 0x0000, // Device Control E1000_STATUS = 0x0008, // Device Status E1000_RCTL = 0x0100, // Receive Control E1000_TCTL = 0x0400, // Transmit Control E1000_FCAL = 0x0028, // Flow Control Address Low E1000_FCAH = 0x002C, // Flow Control Address High E1000_FCT = 0x0030, // Flow Control Type E1000_FCTTV = 0x0170, // Flow Control Transmit Timer Value E1000_FCRTL = 0x2160, // Flow Control Receive Threshold Low E1000_FCRTH = 0x2168, // Flow Control Receive Threshold High E1000_XONRXC = 0x4048, // XON Received Count E1000_XONTXC = 0x404C, // XON Transmitted Count E1000_XOFFRXC = 0x4050, // XOFF Received Count E1000_XOFFTXC = 0x4054, // XOFF Transmitted Count }; int my_get_info( char *buf, char **start, off_t off, int count ) { static int n_xon_rx = 0, n_xon_tx = 0; static int n_xoff_rx = 0, n_xoff_tx = 0; int len = 0; n_xon_rx += ioread32( io + E1000_XONRXC ); n_xon_tx += ioread32( io + E1000_XONTXC ); n_xoff_rx += ioread32( io + E1000_XOFFRXC ); n_xoff_tx += ioread32( io + E1000_XOFFTXC ); len += sprintf( buf+len, "\n" ); len += sprintf( buf+len, " XON frames sent = %d ", n_xon_tx ); len += sprintf( buf+len, " XON frames received = %d \n", n_xon_rx ); len += sprintf( buf+len, " XOFF frames sent = %d ", n_xoff_tx ); len += sprintf( buf+len, " XOFF frames received = %d \n", n_xoff_rx ); len += sprintf( buf+len, "\n" ); if ( off == 0 ) iowrite32( ioread32( io + E1000_TCTL )|(1<<22), io + E1000_TCTL ); else off = 1; return len; } static int __init trypause_init( void ) { unsigned int i, dev_control, tx_control; u16 pci_cmd; printk( "<1>\nInstalling \'%s\' module\n", modname ); devp = pci_get_device( VENDOR_ID, DEVICE_ID, NULL ); if ( !devp ) return -ENODEV; mmio_base = pci_resource_start( devp, 0 ); mmio_size = pci_resource_len( devp, 0 ); io = ioremap( mmio_base, mmio_size ); if ( !io ) return -ENOSPC; pci_read_config_word( devp, 4, &pci_cmd ); pci_cmd |= (1<<2); pci_write_config_word( devp, 4, pci_cmd ); for (i = 0x4000; i < 0x4200; i+=4 ) ioread32( io + i ); iowrite32( 0x00000000, io + E1000_STATUS ); dev_control = 0; dev_control |= (1<<0); // FD-bit (Full Duplex) dev_control |= (1<<6); // SLU-bit (Set Link Up) dev_control |= (2<<8); // SPEED-bits (2=1000Mbps) dev_control |= (1<<18); // D/UD Status dev_control |= (1<<26); // RST-bit (Device Reset) dev_control |= (1<<27); // RFCE-bit (Rx Flow Control Enable) dev_control |= (1<<28); // TFCE-bit (Tx Flow Control Enable) dev_control |= (0<<30); // VME-bit (VLAN Enable) dev_control |= (0<<31); // PHY_RST-bit (PHY Reset) iowrite32( dev_control, io + E1000_CTRL ); dev_control &= ~(1<<26); iowrite32( dev_control, io + E1000_CTRL ); udelay( 10000 ); while ( (ioread32( io + E1000_STATUS )&3) != 3 ); printk( " CTRL=%08X ", ioread32( io + E1000_CTRL ) ); printk( " STATUS=%08X \n", ioread32( io + E1000_STATUS ) ); // initialize the Flow Control registers iowrite32( 0x00C28001, io + E1000_FCAL ); iowrite32( 0x00000100, io + E1000_FCAH ); iowrite32( 0x00008808, io + E1000_FCT ); iowrite32( 0x00008818, io + E1000_FCT ); iowrite32( 0x00000680, io + E1000_FCTTV ); iowrite32( 0x000047F8, io + E1000_FCRTL ); iowrite32( 0x00004800, io + E1000_FCRTH ); tx_control = 0; tx_control |= (0<<1); // EN-bit (Transmit Enable) tx_control |= (1<<3); // PSP-bit (Pad Short Packets) tx_control |= (15<<4); // CT=15 (Collision Threshold) tx_control |= (63<<12); // COLD=63 (Collision Distance) tx_control |= (0<<22); // SWXOFF-bit (Software XOFF) tx_control |= (1<<24); // RTLC-bit (Re-Transmit on Late Collision) tx_control |= (0<<25); // UNORTX-bit (Underrun No Re-Transmit) tx_control |= (0<<26); // TXCSCMT (TxDesc Minimum Threshold) tx_control |= (0<<28); // MULR-bit (Multiple Request Support) tx_control |= (0<<29); // undocumented function iowrite32( tx_control, io + E1000_TCTL ); tx_control |= (1<<1); iowrite32( tx_control, io + E1000_TCTL ); tx_control |= (1<<22); // SWXOFF-bit iowrite32( tx_control, io + E1000_TCTL ); create_proc_info_entry( modname, 0, NULL, my_get_info ); return 0; //SUCCESS } static void __exit trypause_exit(void ) { remove_proc_entry( modname, NULL ); iounmap( io ); printk( "<1>Removing \'%s\' module\n", modname ); } module_init( trypause_init ); module_exit( trypause_exit ); MODULE_LICENSE("GPL");