//------------------------------------------------------------------- // nicecho.c // // This module implements an 'echo' service for the 82573L nic. // Each ethernet packet received by the network controller will // be transmitted back to its sender. A pseudo-file will allow // the user to examine the descriptors in the RX and TX queues. // // NOTE: Written and tested with Linux kernel version 2.6.22.5. // // programmer: ALLAN CRUSE // date begun: 08 JAN 2008 // completion: 04 FEB 2008 //------------------------------------------------------------------- #include // for init_module() #include // for create_proc_info_entry() #include // for pci_get_device() #include // for request_irq() #define VENDOR_ID 0x8086 // Intel Corporation #define DEVICE_ID 0x109A // 82573L controller //#define DEVICE_ID 0x10B9 // 82572EI controller //#define DEVICE_ID 0x107C // 82541PI controller #define KMEM_SIZE (4<<12) // kernel memory used enum { E1000_CTRL = 0x0000, // Device Control E1000_STATUS = 0x0008, // Device Status E1000_CTRL_EXT = 0x0018, // Device Control Extended E1000_ICR = 0x00C0, // Interrupt Cause Read E1000_IMS = 0x00D0, // Interrupt Mask Set E1000_IMC = 0x00D8, // Interrupt Mask Clear E1000_RCTL = 0x0100, // Receive Control E1000_TCTL = 0x0400, // Transmit Control E1000_TIPG = 0x0410, // Transmit Inter-Packet Gap E1000_RDBAL = 0x2800, // RX Desc-queue Base-Addr Low E1000_RDBAH = 0x2804, // RX Desc-queue Base-Addr High E1000_RDLEN = 0x2808, // RX Desc-queue Length E1000_RDH = 0x2810, // RX Desc-queue Head E1000_RDT = 0x2818, // RX Desc-queue Tail E1000_RXDCTL = 0x2828, // RX Desc-queue Control E1000_TDBAL = 0x3800, // TX Desc-queue Base-Addr Low E1000_TDBAH = 0x3804, // TX Desc-queue Base-Addr High E1000_TDLEN = 0x3808, // TX Desc-queue Length E1000_TDH = 0x3810, // TX Desc-queue Head E1000_TDT = 0x3818, // TX Desc-queue Tail E1000_TXDCTL = 0x3828, // TX Desc-queue Control E1000_TPR = 0x40D0, // Total Packets Received E1000_TPT = 0x40D4, // Total Packets Transmitted E1000_RA = 0x5400, // Receive-address Array }; typedef struct { unsigned long long base_address; unsigned short packet_length; unsigned short frame_checksum; unsigned char desc_status; unsigned char desc_errors; unsigned short vlan_tag; } RX_DESCRIPTOR; typedef struct { unsigned long long base_address; unsigned short packet_length; unsigned char cksum_offset; unsigned char desc_command; unsigned char desc_status; unsigned char cksum_origin; unsigned short special_info; } TX_DESCRIPTOR; char modname[] = "nicecho"; struct pci_dev *devp; unsigned char mac[6]; unsigned long mmio_base; unsigned long mmio_size; unsigned long kmem_phys; int irq; void *io, *kmem; RX_DESCRIPTOR *rxring; TX_DESCRIPTOR *txring; int rx_packets, tx_packets; irqreturn_t nic_isr( int irq, void *dev_id ) { static int reps = 0; int intr_cause = ioread32( io + E1000_ICR ); if ( intr_cause == 0 ) return IRQ_NONE; printk( " NICECHO: #%d cause=%08X \n", ++reps, intr_cause ); if ( intr_cause & (1<<7) ) // Receiver Timeout: a new packet arrived { int index = ioread32( io + E1000_TDT ); unsigned long where = txring[ index ].base_address; unsigned char *pktp = phys_to_virt( where ); memcpy( pktp+0, pktp+6, 6 ); // source becomes destination memcpy( pktp+6, mac, 6 ); // our station becomes source txring[ index ].desc_status = 0; txring[ index ].packet_length = rxring[ index ].packet_length; index = (1 + index) % 8; // ring-buffer index advances iowrite32( index, io + E1000_TDT ); } iowrite32( intr_cause, io + E1000_ICR ); return IRQ_HANDLED; } int my_get_info( char *buf, char **start, off_t off, int count ) { int rdba = ioread32( io + E1000_RDBAL ); int rxhead = ioread32( io + E1000_RDH ); int rxtail = ioread32( io + E1000_RDT ); int tdba = ioread32( io + E1000_TDBAL ); int txhead = ioread32( io + E1000_TDH ); int txtail = ioread32( io + E1000_TDT ); int i, len = 0; rx_packets += ioread32( io + E1000_TPR ); tx_packets += ioread32( io + E1000_TPT ); len += sprintf( buf+len, "\n Receive-Descriptor Buffer-Area " ); len += sprintf( buf+len, "(head=%d, tail=%d) \n", rxhead, rxtail ); for (i = 0; i < 8; i++) { int status = rxring[i].desc_status; len += sprintf( buf+len, " #%-2d ", i ); len += sprintf( buf+len, "%08X: ", rdba + 16*i ); len += sprintf( buf+len, "%016llX ", rxring[i].base_address ); len += sprintf( buf+len, "%04X ", rxring[i].packet_length ); len += sprintf( buf+len, "%04X ", rxring[i].frame_checksum ); len += sprintf( buf+len, "%02X ", rxring[i].desc_status ); len += sprintf( buf+len, "%02X ", rxring[i].desc_errors ); len += sprintf( buf+len, "%04X ", rxring[i].vlan_tag ); len += sprintf( buf+len, " " ); if ( status & (1<<0) ) len += sprintf( buf+len, "DD " ); if ( status & (1<<1) ) len += sprintf( buf+len, "EOP " ); if ( status & (1<<2) ) len += sprintf( buf+len, "IXSM " ); if ( status & (1<<3) ) len += sprintf( buf+len, "VP " ); if ( status & (1<<5) ) len += sprintf( buf+len, "TCPCS " ); if ( status & (1<<6) ) len += sprintf( buf+len, "IPCS " ); if ( status & (1<<7) ) len += sprintf( buf+len, "PIF " ); len += sprintf( buf+len, "\n" ); } len += sprintf( buf+len, "\n Transmit-Descriptor Buffer-Area " ); len += sprintf( buf+len, "(head=%d, tail=%d) \n", txhead, txtail ); for (i = 0; i < 8; i++) { int status = txring[i].desc_status; len += sprintf( buf+len, " #%-2d ", i ); len += sprintf( buf+len, "%08X: ", tdba + 16*i ); len += sprintf( buf+len, "%016llX ", txring[i].base_address ); len += sprintf( buf+len, "%04X ", txring[i].packet_length ); len += sprintf( buf+len, "%02X ", txring[i].cksum_offset ); len += sprintf( buf+len, "%02X ", txring[i].desc_command ); len += sprintf( buf+len, "%02X ", txring[i].desc_status ); len += sprintf( buf+len, "%02X ", txring[i].cksum_origin ); len += sprintf( buf+len, "%04X ", txring[i].special_info ); len += sprintf( buf+len, " " ); if ( status & (1<<0) ) len += sprintf( buf+len, "DD " ); if ( status & (1<<1) ) len += sprintf( buf+len, "EC " ); if ( status & (1<<2) ) len += sprintf( buf+len, "LC " ); len += sprintf( buf+len, "\n" ); } len += sprintf( buf+len, "\n" ); len += sprintf( buf+len, " total incoming packets: %d ", rx_packets ); len += sprintf( buf+len, " total outgoing packets: %d ", tx_packets ); len += sprintf( buf+len, "\n\n" ); return len; } static int __init my_init( void ) { int i, rx_control, tx_control; u16 pci_cmd; printk( "<1>\nInstalling \'%s\' module\n", modname ); // detect the Intel Pro1000 network interface controller devp = pci_get_device( VENDOR_ID, DEVICE_ID, NULL ); if ( !devp ) return -ENODEV; // map the device's i/o-memory to kernel-space addresses mmio_base = pci_resource_start( devp, 0 ); mmio_size = pci_resource_len( devp, 0 ); io = ioremap_nocache( mmio_base, mmio_size ); if ( !io ) return -ENOSPC; // copy the device's unique hardware MAC-address to 'mac[]' memcpy( mac, io + E1000_RA, 6 ); // allocate and clear an aligned region of kernel memory kmem = kzalloc( KMEM_SIZE, GFP_KERNEL ); if ( !kmem ) { iounmap( io ); return -ENOMEM; } // setup array-pointers to our RX and TX descriptor-queues kmem_phys = virt_to_phys( kmem ); rxring = phys_to_virt( kmem_phys + 8 * 0x600 + 0x100 ); txring = phys_to_virt( kmem_phys + 8 * 0x600 + 0x200 ); // initialize the entries in our RX and TX descriptor-queues for (i = 0; i < 8; i++) { rxring[i].base_address = kmem_phys + i*0x600; rxring[i].packet_length = 0; rxring[i].frame_checksum = 0; rxring[i].desc_status = 0; rxring[i].desc_errors = 0; rxring[i].vlan_tag = 0; txring[i].base_address = kmem_phys + i*0x600; txring[i].packet_length = 0; txring[i].desc_command = (1<<0)|(1<<1)|(1<<3); // EOP,IFCS,RS txring[i].desc_status = 0; txring[i].cksum_offset = 0; txring[i].cksum_origin = 0; txring[i].special_info = 0; } // insure the device's Bus Master capability is enabled pci_read_config_word( devp, 4, &pci_cmd ); pci_cmd |= (1<<2); pci_write_config_word( devp, 4, pci_cmd ); // perform a 'reset' of the network controller's state iowrite32( 0x00000000, io + E1000_STATUS ); iowrite32( 0xFFFFFFFF, io + E1000_IMC ); iowrite32( 0x040C0241, io + E1000_CTRL ); iowrite32( 0x000C0241, io + E1000_CTRL ); udelay( 10000 ); // install interrupt-handler and enable desired NIC interrupts irq = devp->irq; iowrite32( 0xFFFFFFFF, io + E1000_ICR ); if ( request_irq( irq, nic_isr, IRQF_SHARED, modname, &modname ) < 0 ) { iounmap( io ); kfree( kmem ); return -EBUSY; } iowrite32( 0x00000080, io + E1000_IMS ); // configure the 'receive' engine and its RX descriptor-queue rx_control = 0; rx_control |= (0<<1); // EN-bit (Enable) rx_control |= (1<<2); // SBP-bit (Store Bad Packets) rx_control |= (1<<3); // UPE-bit (Unicast Promiscuous Enable) rx_control |= (1<<4); // MPE-bit (Multicast Promiscuous Enable) rx_control |= (1<<15); // BAM-bit (Broadcast Address Enable) rx_control |= (1<<23); // PMCF-bit (Pass MAC Control Frames) rx_control |= (1<<26); // SECRC-bit (Strip Ethernet CRC) iowrite32( rx_control, io + E1000_RCTL ); iowrite32( virt_to_phys( rxring ), io + E1000_RDBAL ); iowrite32( 0x00000000, io + E1000_RDBAH ); iowrite32( 0x00000080, io + E1000_RDLEN ); iowrite32( 0x01010000, io + E1000_RXDCTL ); // configure the 'transmit' engine and its TX descriptor-queue tx_control = 0; tx_control |= (0<<1); // EN-bit (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 |= (1<<24); // RTLC-bit (Re-Transmit on Late Collisions) iowrite32( tx_control, io + E1000_TCTL ); iowrite32( virt_to_phys( txring ), io + E1000_TDBAL ); iowrite32( 0x00000000, io + E1000_TDBAH ); iowrite32( 0x00000080, io + E1000_TDLEN ); iowrite32( 0x01010000, io + E1000_TXDCTL ); iowrite32( 0x001401C0, io + E1000_CTRL_EXT ); iowrite32( 0x00602008, io + E1000_TIPG ); // create a pseudo-file (so users can inspect our descriptors) create_proc_info_entry( modname, 0, NULL, my_get_info ); // enable the controller's 'transmit' engine tx_control |= (1<<1); // EN-bit (Enable) iowrite32( tx_control, io + E1000_TCTL ); // enable the controller's 'receive' engine rx_control |= (1<<1); // EN-bit (Enable) iowrite32( rx_control, io + E1000_RCTL ); // give ownership of our RX rescriptors to the controller iowrite32( 0x00000008, io + E1000_RDT ); return 0; //SUCCESS } static void __exit my_exit(void ) { int rx_control, tx_control; // delete our pseudo-file from the '/proc' directory remove_proc_entry( modname, NULL ); // disable the controller's Receive engine rx_control = ioread32( io + E1000_RCTL ); rx_control &= ~(1<<1); iowrite32( rx_control, io + E1000_RCTL ); // disable the controller's Transmit engine tx_control = ioread32( io + E1000_TCTL ); tx_control &= ~(1<<1); iowrite32( tx_control, io + E1000_TCTL ); // disable the controller's interrupts iowrite32( 0xFFFFFFFF, io + E1000_IMC ); iowrite32( 0xFFFFFFFF, io + E1000_ICR ); // remove our interrupt-handher free_irq( irq, modname ); // free our allocation of kernel memory kfree( kmem ); // unmap our controller's i/o-memory iounmap( io ); printk( "<1>Removing \'%s\' module\n", modname ); } module_init( my_init ); module_exit( my_exit ); MODULE_LICENSE("GPL");