//------------------------------------------------------------------- // nictx.c // // This Linux Kernel Module is written with the aim of showing // what "minimal" sequence of programming steps is required in // order for our Intel 82573L Ethernet controller to transmit. // A pseudo-file (named '/proc/nictx') can be viewed to verify // that a packet got sent, and our 'nicwatch' utility can show // us whether our 'broadcast' packet indeed has been received. // // NOTE: written and tested for Linux kernel version 2.6.26.6. // // programmer: ALLAN CRUSE // written on: 24 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_TX_DESC 8 // Number of TX-descriptors #define TX_BUFLEN 2048 // Size of each packet-buffer #define TX_MEMSIZ (N_TX_DESC * (16 + TX_BUFLEN)) #define KMEM_SIZE TX_MEMSIZ 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; enum { E1000_CTRL = 0x0000, // Device Control E1000_STATUS = 0x0008, // Device Status E1000_TCTL = 0x0400, // Transmit Control E1000_TDBAL = 0x3800, // Tx-Descriptor Base-Address Low E1000_TDBAH = 0x3804, // Tx-Descriptor Base-Address High E1000_TDLEN = 0x3808, // Tx-Descriptor queue Length E1000_TDH = 0x3810, // Tx-Descriptor Head E1000_TDT = 0x3818, // Tx-Descriptor Tail E1000_TXDCTL = 0x3828, // Tx-Descriptor Control E1000_RA = 0x5400, // Receive-address Array }; char modname[] = "nictx"; char devname[] = "nic"; char info_tx[] = "nictx"; 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; TX_DESCRIPTOR *txring; int my_proc_tx( char *buf, char **start, off_t off, int count, int *eof, void *data ) { int txtail, txhead, i, len = 0; txhead = ioread32( io + E1000_TDH ); txtail = ioread32( io + E1000_TDT ); len += sprintf( buf+len, "\n Transmit-Descriptor Buffer-Area " ); len += sprintf( buf+len, "(head=%d, tail=%d) \n\n", txhead, txtail ); for (i = 0; i < N_TX_DESC; i++) { int status = txring[ i ].desc_status; int command = txring[ i ].desc_command; unsigned long desc_addr = virt_to_phys( txring + i ); len += sprintf( buf+len, " #%-2d ", i ); len += sprintf( buf+len, "%08lX: ", desc_addr ); 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 ); 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, " " ); if ( command & (1<<0) ) len += sprintf( buf+len, "EOP " ); if ( command & (1<<1) ) len += sprintf( buf+len, "IFCS " ); if ( command & (1<<2) ) len += sprintf( buf+len, "ICP " ); if ( command & (1<<3) ) len += sprintf( buf+len, "RS " ); if ( command & (1<<6) ) len += sprintf( buf+len, "VLE " ); if ( command & (1<<7) ) len += sprintf( buf+len, "IDE " ); len += sprintf( buf+len, "\n" ); } len += sprintf( buf+len, "\n" ); len += sprintf( buf+len, "\n" ); return len; } static int __init nictx_init( void ) { u16 pcicmd; int i, dev_control, tx_control; unsigned long txmem_phys, txbuff; unsigned char *cp; // 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 ); txring = (TX_DESCRIPTOR *)kmem; txmem_phys = virt_to_phys( txring ); txbuff = txmem_phys + 16 * N_TX_DESC; for (i = 0; i < N_TX_DESC; i++) { txring[ i ].base_address = txbuff + i * TX_BUFLEN; txring[ i ].desc_command = (0<<6)|(1<<3)|(1<<1)|(1<<0); txring[ 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 'Transmit' engine 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 |= (0<<22); // SWXOFF-bit (Software XOFF) tx_control |= (1<<24); // RTLC=1 (Re-Transmit on Late Collisions) tx_control |= (0<<25); // UNORTX-bit (Underrun No ReTransmit) tx_control |= (0<<26); // TXCSCMT=0 (TxDesc Min Threshold) tx_control |= (0<<28); // MULR-bit (Multiple Request Support) iowrite32( tx_control, io + E1000_TCTL ); iowrite32( txmem_phys >> 0, io + E1000_TDBAL ); iowrite32( txmem_phys >> 32, io + E1000_TDBAH ); iowrite32( N_TX_DESC << 4, io + E1000_TDLEN ); iowrite32( 0x01010000, io + E1000_TXDCTL ); // forulate contents for an initial packet cp = (unsigned char*)phys_to_virt( txring[ 0 ].base_address ); memcpy( cp+0, dst, 6 ); memcpy( cp+6, mac, 6 ); *(unsigned short*)( cp+12 ) = htons( 0x40 ); memset( cp+14, 0x55, 0x40 ); txring[0].packet_length = 0x50; iowrite32( 1, io + E1000_TDT ); // enable the Transmit engine iowrite32( ioread32( io + E1000_TCTL ) | (1<<1), io + E1000_TCTL ); // install our driver's pseudo-file in the '/proc' directory create_proc_read_entry( info_tx, 0, NULL, my_proc_tx, NULL ); return 0; //SUCCESS } static void __exit nictx_exit(void ) { // disable the Transmit engine iowrite32( ioread32( io + E1000_TCTL ) & ~(1<<1), io + E1000_TCTL ); // delete our pseudo-file from the '/proc' directory remove_proc_entry( info_tx, 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( nictx_init ); module_exit( nictx_exit ); MODULE_LICENSE("GPL");