//------------------------------------------------------------------- // nicrx.c // // This Linux Kernel Module is written with the aim of showing // what "minimal" sequence of programming steps is required in // order that our Intel 82573L Ethernet controller can receive // network packets. A pseudo-file (named '/proc/nicrx') shows // the current state of the circular Receive Descriptor queue, // and if our 'dram.c' device-driver was installed, a user can // view the packet-buffer memory by using our 'fileview' tool. // // NOTE: Written and yested for Linux kernel version 2.6.26.6. // // programmer: ALLAN CRUSE // written on: 25 APR 2009 //------------------------------------------------------------------- #include // for init_module() #include // for create_proc_read_entry() #include // for pci_get_device() #define VENDOR_ID 0x8086 // Intel Corporation #define DEVICE_ID 0x109A // 82573L controller #define N_RX_DESC 8 // Number of Rx-descriptors #define RX_BUFLEN 2048 // Size of each packet-buffer #define RX_MEMSIZ (N_RX_DESC * (16 + RX_BUFLEN)) #define KMEM_SIZE RX_MEMSIZ typedef struct { unsigned long long base_address; unsigned short packet_length; unsigned short packet_chksum; unsigned char desc_status; unsigned char desc_errors; unsigned short vlan_tag; } RX_DESCRIPTOR; enum { E1000_CTRL = 0x0000, // Device Control E1000_STATUS = 0x0008, // Device Status E1000_RCTL = 0x0100, // Receive Control E1000_RDBAL = 0x2800, // Rx-Descriptor Base-Address Low E1000_RDBAH = 0x2804, // Rx-Descriptor Base-Address High E1000_RDLEN = 0x2808, // Rx-Descriptor queue Length E1000_RDH = 0x2810, // Rx-Descriptor Head E1000_RDT = 0x2818, // Rx-Descriptor Tail E1000_RXDCTL = 0x2828, // Rx-Descriptor Control E1000_RA = 0x5400, // Receive-address Array }; char modname[] = "nicrx"; char devname[] = "nic"; char info_rx[] = "nicrx"; unsigned char mac[6], dst[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; int my_major = 97; struct pci_dev *devp; unsigned long mmio_base; unsigned long mmio_size; void *io; void *kmem; unsigned long kmem_phys; RX_DESCRIPTOR *rxring; int my_proc_rx( char *buf, char **start, off_t off, int count, int *eof, void *data ) { int rxtail, rxhead, i, len = 0; rxhead = ioread32( io + E1000_RDH ); rxtail = ioread32( io + E1000_RDT ); len += sprintf( buf+len, "\n Receive-Descriptor Buffer-Area " ); len += sprintf( buf+len, "(head=%d, tail=%d) \n\n", rxhead, rxtail ); for (i = 0; i < N_RX_DESC; i++) { int status = rxring[ i ].desc_status; int errors = rxring[ i ].desc_errors; unsigned long desc_addr = virt_to_phys( rxring + i ); len += sprintf( buf+len, " #%-2d ", i ); len += sprintf( buf+len, "%08lX: ", desc_addr ); 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].packet_chksum ); 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 ); 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, " " ); if ( errors & (1<<0) ) len += sprintf( buf+len, "CE " ); if ( errors & (1<<0) ) len += sprintf( buf+len, "FE " ); if ( errors & (1<<0) ) len += sprintf( buf+len, "TCPE " ); if ( errors & (1<<0) ) len += sprintf( buf+len, "IPE " ); if ( errors & (1<<0) ) len += sprintf( buf+len, "RXE " ); len += sprintf( buf+len, "\n" ); } len += sprintf( buf+len, "\n" ); return len; } static int __init nicrx_init( void ) { u16 pcicmd; int i, dev_control, rx_control; unsigned long rxmem_phys, rxbuff; // write confirmation-message to the kernel's log-file printk( "<1>\nInstalling \'%s\' module ", modname ); printk( "(major=%d) \n", my_major ); // check that the Intel 82573L controller is installed devp = pci_get_device( VENDOR_ID, DEVICE_ID, NULL ); if ( !devp ) return -ENODEV; // setup memory-mapping of the controller's registers 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 Ethernet controller's MAC-address memcpy( mac, io + E1000_RA, 6 ); printk( " Intel PRO1000 Ethernet Interface - MAC=" ); for (i = 0; i < 6; i++) printk( "%02X%c", mac[i], (i<5) ? ':' : ' ' ); printk( "\n" ); // allocate kernel memory for descriptors and buffers kmem = kzalloc( KMEM_SIZE, GFP_KERNEL | GFP_DMA ); if ( !kmem ) { iounmap( io ); return -ENOMEM; } kmem_phys = virt_to_phys( kmem ); printk( " kernel memory allocated at physical address-range " ); printk( "0x%08lX-0x%08lX \n", kmem_phys, kmem_phys + KMEM_SIZE ); rxring = (RX_DESCRIPTOR *)kmem; rxmem_phys = virt_to_phys( rxring ); rxbuff = rxmem_phys + 16 * N_RX_DESC; for (i = 0; i < N_RX_DESC; i++) { rxring[ i ].base_address = rxbuff + i * RX_BUFLEN; rxring[ i ].desc_status = 0; } // insure Bus Master capability is enabled pci_read_config_word( devp, 4, &pcicmd ); pcicmd |= (1 << 2); pci_write_config_word( devp, 4, pcicmd ); // setup the configuration-options for 'Device Control' dev_control = 0; dev_control |= (1<<0); // FD-bit (Full Duplex) dev_control |= (0<<2); // GIOMD-bit (GIO Master Disable) dev_control |= (1<<3); // LRST-bit (Link Reset) dev_control |= (1<<6); // SLU-bit (Set Link Up) dev_control |= (2<<8); // SPEED=2 (1000Mbps) dev_control |= (1<<11); // FRCSPD-bit (Force Speed) dev_control |= (0<<12); // FRCDPLX-bit (Force Duplex) dev_control |= (0<<20); // ADVD3WUC-bit (Advertise D3 Wake Up Cap) dev_control |= (1<<26); // RST-bit (Device Reset) dev_control |= (0<<27); // RFCE-bit (Receive Flow Control Enable) dev_control |= (0<<28); // TFCE-bit (Transmit Flow Control Enable) dev_control |= (0<<30); // VME-bit (VLAN Mode Enable) dev_control |= (0<<31); // PHY_RST-bit (PHY Reset) // perform a 'reset' of the network controller iowrite32( 0x00000000, io + E1000_STATUS ); iowrite32( dev_control | (1<<26), io + E1000_CTRL ); iowrite32( dev_control & ~(1<<26), io + E1000_CTRL ); udelay( 10000 ); // setup configuration-options for the 'Receive' engine 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 |= (0<<5); // LPE-bit (Long Packet Enable) rx_control |= (0<<6); // LBM=0 (LoopBack Mode) rx_control |= (2<<8); // RDMTS=2 (Rx-Descriptor Min Thresh Size) rx_control |= (0<<10); // DTYPE=0 (Descriptor Type) rx_control |= (0<<12); // MO=0 (Multicast Offset) rx_control |= (1<<15); // BAM-bit (Broadcast Address Enable) rx_control |= (0<<16); // BSIZE=0 (Buffer Size) rx_control |= (0<<18); // VLE-bit (VLAN-filter Enable) rx_control |= (0<<19); // CFIEN-bit (Canonical Form Indicator Enable) rx_control |= (0<<20); // CI=0 (Canonical Form Indicator bit-value) rx_control |= (1<<22); // DPF-bit (Discard Pause Frames) rx_control |= (0<<23); // PMCF-bit (Pass MAC Control Frames) rx_control |= (0<<25); // BSEC-bit (Buffer Size Extension) rx_control |= (1<<26); // SECRC-bit (Strip Ethernet CRC) rx_control |= (2<<27); // FLEXBUF=2K (Flexible Buffer Size) iowrite32( rx_control, io + E1000_RCTL ); iowrite32( rxmem_phys >> 0, io + E1000_RDBAL ); iowrite32( rxmem_phys >> 32, io + E1000_RDBAH ); iowrite32( N_RX_DESC << 4, io + E1000_RDLEN ); iowrite32( 0x01010000, io + E1000_RXDCTL ); // now give the nic "ownership" of all our RX-descriptors iowrite32( N_RX_DESC, io + E1000_RDT ); // enable the Receive engine iowrite32( ioread32( io + E1000_RCTL ) | (1<<1), io + E1000_RCTL ); // install our driver's pseudo-file in the '/proc' directory create_proc_read_entry( info_rx, 0, NULL, my_proc_rx, NULL ); return 0; //SUCCESS } static void __exit nicrx_exit(void ) { // disable the Receive engine iowrite32( ioread32( io + E1000_RCTL ) & ~(1<<1), io + E1000_RCTL ); // delete our pseudo-file from the '/proc' directory remove_proc_entry( info_rx, NULL ); // release our allocation of kernel memory kfree( kmem ); // unmap the nic's device-registers iounmap( io ); // write confirmation-message to the kernel's log-file printk( "<1>Removing \'%s\' module\n", modname ); } module_init( nicrx_init ); module_exit( nicrx_exit ); MODULE_LICENSE("GPL");