#include "rtl8139.h" #include "string.h" #include "stdio.h" #include "stdlib.h" #define DEBUG static rtl8139_vtable_t const rtl8139_vtable = { { { rtl8139_activate, rtl8139_deactivate, rtl8139_deinit, rtl8139_print_name, rtl8139_print_info }, network_register_handler, rtl8139_write } }; // registers #define REG_MAC0 0x00 #define REG_TR_STATUS0 0x10 #define REG_TR_ADDR0 0x20 #define REG_RBSTART 0x30 #define REG_CMD 0x37 #define REG_CAPR 0x38 #define REG_IMR 0x3C #define REG_ISR 0x3E #define REG_TCR 0x40 #define REG_RCR 0x44 #define REG_MPC 0x4C // commands for REG_CMD (command register) #define CMD_RESET 0x10 #define CMD_RECEIVER_ENABLE 0x08 #define CMD_TRANSMITTER_ENABLE 0x04 #define CMD_BUFFER_EMPTY 0x01 // mask for reading the version in from the REG_TCR (transmit configuration register) #define TCR_MODEL_VERSION_AM 0x7C000000 #define TCR_MODEL_VERSION_BM 0x00C00000 // vendor identifiers received from the REG_TCR (transmit configuration register) #define TCR_MODEL_VERSION_RTL8139 0x60000000 #define TCR_MODEL_VERSION_RTL8139A 0x70000000 #define TCR_MODEL_VERSION_RTL8139B 0x78000000 #define TCR_MODEL_VERSION_RTL8130 0x78000000 #define TCR_MODEL_VERSION_RTL8139AG 0x74000000 #define TCR_MODEL_VERSION_RTL8139C 0x74000000 #define TCR_MODEL_VERSION_RTL8100 0x78800000 #define TCR_MODEL_VERSION_RTL8100B 0x74400000 #define TCR_MODEL_VERSION_RTL8139CP 0x74800000 #define TCR_MODEL_VERSION_RTL8101 0x74C00000 // values for REG_TCR (transmit configuration register) #define TCR_IFG_STANDARD 0x03000000 #define TCR_MXDMA_2048 0x00000700 #define TCR_APPEND_CRC 0x00010000 // values for REG_RCR (receive configuration register) #define RCR_MXDMA_UNLIMITED 0x00000700 #define RCR_RECEIVE_BUFFER_8K_16 0x00000000 #define RCR_WRAP 0x00000080 #define RCR_ACCEPT_BROADCAST 0x00000008 #define RCR_ACCEPT_MULTICAST 0x00000004 #define RCR_ACCEPT_PHYS_MATCH 0x00000002 // values for REG_IMR/REG_ISR (interrupt mask/status registers) #define ISR_SYSTEM_ERROR 0x8000 #define ISR_TIMEOUT 0x4000 #define ISR_CABLE_LENGTH_CHANGED 0x2000 #define ISR_RECEIVE_FIFO_OVERFLOW 0x0040 #define ISR_PACKET_UNDERRUN_LINK_CHANGED 0x0020 #define ISR_RECEIVE_BUFFER_OVERFLOW 0x0010 #define ISR_TRANSMIT_ERROR 0x0008 #define ISR_TRANSMIT_OK 0x0004 #define ISR_RECEIVE_ERROR 0x0002 #define ISR_RECEIVE_OK 0x0001 #define ISR_ALL \ ISR_CABLE_LENGTH_CHANGED | ISR_TIMEOUT | ISR_SYSTEM_ERROR | \ ISR_RECEIVE_FIFO_OVERFLOW | ISR_PACKET_UNDERRUN_LINK_CHANGED | \ ISR_RECEIVE_BUFFER_OVERFLOW | ISR_TRANSMIT_ERROR | ISR_TRANSMIT_OK | \ ISR_RECEIVE_ERROR | ISR_RECEIVE_OK // header #define PACKET_HEADER_ROK 0x0001 // receive buffer, set to 8k + header (16) + wrap (2k) #define RECEIVE_BUFFER_SIZE 8192 #define RECEIVE_TOTAL_BUFFER_SIZE RECEIVE_BUFFER_SIZE + 16 + 2048 void rtl8139_init( rtl8139_t *rtl8139, pci_controller_t *pci, pci_device_descriptor_t *descriptor, interrupt_t *interrupt, void *context ) { memset( rtl8139, 0, sizeof( rtl8139_t ) ); network_init( (network_t *)rtl8139, interrupt, context ); rtl8139->pci = pci; rtl8139->pci_descriptor = *descriptor; puts( "Initializing driver for Realtek 8139 network card.." ); for( int i = 0; i < NOF_MAC_REGISTERS; i++ ) { port8_init( &rtl8139->MAC_port[i], descriptor->port_base + REG_MAC0 + i ); } for( int i = 0; i < NOF_TRANSMIT_BUFFERS; i++ ) { port32_init( &rtl8139->TSD_port[i], descriptor->port_base + REG_TR_STATUS0 +i ); port32_init( &rtl8139->TSAD_port[i], descriptor->port_base + REG_TR_ADDR0 + i ); } port8_init( &rtl8139->CMD_port, descriptor->port_base + REG_CMD ); port16_init( &rtl8139->CAPR_port, descriptor->port_base + REG_CAPR ); port32_init( &rtl8139->RBSTART_port, descriptor->port_base + REG_RBSTART ); port16_init( &rtl8139->IMR_port, descriptor->port_base + REG_IMR ); port16_init( &rtl8139->ISR_port, descriptor->port_base + REG_ISR ); port32_init( &rtl8139->TCR_port, descriptor->port_base + REG_TCR ); port32_init( &rtl8139->RCR_port, descriptor->port_base + REG_RCR ); port32_init( &rtl8139->MPC_port, descriptor->port_base + REG_MPC ); // software reset, the CR_RESET bit remains high till the // reset is finished port8_write( &rtl8139->CMD_port, CMD_RESET ); while( ( port8_read( &rtl8139->CMD_port ) & CMD_RESET ) == CMD_RESET ); // get MAC for( int i = 0; i < NOF_MAC_REGISTERS; i++ ) { rtl8139->base.mac_address.byte[i] = port8_read( &rtl8139->MAC_port[i] ); } // enable receiver and transmitter early (TODO: lowlevel says as later as // possible, Osdev and others say as early as possible) port8_write( &rtl8139->CMD_port, CMD_RECEIVER_ENABLE | CMD_TRANSMITTER_ENABLE ); // get model rtl8139->model_id = port32_read( &rtl8139->TCR_port ) & ( TCR_MODEL_VERSION_AM | TCR_MODEL_VERSION_BM ); switch( rtl8139->model_id ) { case TCR_MODEL_VERSION_RTL8139: rtl8139->model = "RTL8139"; break; case TCR_MODEL_VERSION_RTL8139A: rtl8139->model = "RTL8139A"; break; // case TCR_MODEL_VERSION_RTL8139B: case TCR_MODEL_VERSION_RTL8130: rtl8139->model = "RTL8130/RTL8130B"; break; // case TCR_MODEL_VERSION_RTL8139AG: case TCR_MODEL_VERSION_RTL8139C: rtl8139->model = "RTL8139A-G/RTL8139C"; break; case TCR_MODEL_VERSION_RTL8100: rtl8139->model = "RTL8100"; break; case TCR_MODEL_VERSION_RTL8100B: rtl8139->model = "RTL8100B"; break; case TCR_MODEL_VERSION_RTL8139CP: rtl8139->model = "RTL8139C+"; break; case TCR_MODEL_VERSION_RTL8101: rtl8139->model = "RTL8101"; break; default: rtl8139->model = NULL; break; } // initialize TCR/RCR (take Lowlevel suggestions: TCR = 0x03000700, RCR = 0x0000070a) port32_write( &rtl8139->TCR_port, TCR_IFG_STANDARD | TCR_APPEND_CRC | TCR_MXDMA_2048 ); port32_write( &rtl8139->RCR_port, RCR_MXDMA_UNLIMITED | RCR_RECEIVE_BUFFER_8K_16 | RCR_WRAP | RCR_ACCEPT_BROADCAST | RCR_ACCEPT_MULTICAST | RCR_ACCEPT_PHYS_MATCH ); // allocate receive buffer and register it rtl8139->receive_buffer = (uint8_t *)aligned_alloc( 16, RECEIVE_TOTAL_BUFFER_SIZE ); memset( rtl8139->receive_buffer, 0, RECEIVE_TOTAL_BUFFER_SIZE ); port32_write( &rtl8139->RBSTART_port, (uint32_t)rtl8139->receive_buffer ); rtl8139->receive_buffer_pos = 0; // allocate transmit buffers and set index to first transmit buffer for( int i = 0; i < NOF_TRANSMIT_BUFFERS; i++ ) { rtl8139->transmit_buffer[i] = (uint8_t *)aligned_alloc( 16, ETHERNET_MAX_FRAME_SIZE_IEEE_802_3 ); memset( rtl8139->transmit_buffer[i], 0, ETHERNET_MAX_FRAME_SIZE_IEEE_802_3 ); port32_write( &rtl8139->TSAD_port[i], (uint32_t)rtl8139->transmit_buffer[i] ); } rtl8139->transmit_buffer_idx = 0; // reset missed packet counter port32_write( &rtl8139->MPC_port, 0 ); // enable bus mastering for DMA transfers (without this the receive // buffer remains empty!) pci_enable_bus_mastering( rtl8139->pci, &rtl8139->pci_descriptor ); interrupt_handler_init( &rtl8139->interrupt_handler, IRQ_BASE + descriptor->interrupt, interrupt, rtl8139_handle_interrupt, rtl8139 ); interrupts_register_interrupt_handler( rtl8139->interrupt_handler ); // register interrupts we are interested in (we want all and // filter them in the interrupt handler), read ISR to clear // the current values (if there are bits set) port16_write( &rtl8139->IMR_port, ISR_ALL ); (void)port16_read( &rtl8139->ISR_port ); rtl8139_print_info( rtl8139 ); ((driver_t *)rtl8139)->vtable = (driver_vtable_t *)&rtl8139_vtable; } void rtl8139_activate( void *obj ) { puts( "Activating driver for Realtek 8139 network card.." ); rtl8139_t *rtl8139 = obj; } void rtl8139_deactivate( void *obj ) { puts( "Dectivating driver for Realtek 8139 network card.." ); rtl8139_t *rtl8139 = obj; } void rtl8139_deinit( void *obj ) { rtl8139_t *rtl8139 = (rtl8139_t *)obj; free( rtl8139->receive_buffer ); } static void handle_receive_packet( rtl8139_t *rtl8139 ) { while( true ) { uint8_t data = port8_read( &rtl8139->CMD_port ); // still data in the buffer if( data & CMD_BUFFER_EMPTY ) { break; } uint8_t *buffer = rtl8139->receive_buffer + rtl8139->receive_buffer_pos; // packet header uint16_t header = *( (uint16_t *)buffer ); #ifdef DEBUG printf( "rtl8139: rcv header %X\n", header ); #endif if( ( header & PACKET_HEADER_ROK ) == 0 ) { printf( "rtl8139: illegal packet header, no ROK\n", header ); break; } buffer += sizeof( uint16_t ); // buffer is 16-bit alligned uint16_t len = *( (uint16_t *)buffer ); buffer += sizeof( uint16_t ); #ifdef DEBUG printf( "rtl8139: rcv len: %d\n", len ); #endif #ifdef DEBUG uint16_t capr = port16_read( &rtl8139->CAPR_port ); uint32_t rbstart = port32_read( &rtl8139->RBSTART_port ); uint32_t mpc = port32_read( &rtl8139->MPC_port ); printf( "rtl8139: RBSTART: %X, CAPR: %X, RBPOS: %X, MPC: %X\n", rbstart, capr, rtl8139->receive_buffer_pos, mpc ); printf( "rtl8139: " ); for( int i = 0; i < len; i++ ) { printf( "%X ", rtl8139->receive_buffer[rtl8139->receive_buffer_pos+i] ); } puts( "" ); printf( "rtl8139: CRC: " ); for( int i = len; i < len + 4; i++ ) { printf( "%X ", rtl8139->receive_buffer[rtl8139->receive_buffer_pos+i] ); } puts( "" ); #endif network_event_t event; if( len >= ETHERNET_MIN_FRAME_SIZE_IEEE_802_3 ) { // copy data for the handler of the network event event.buf.length = len; event.buf.data = (uint8_t *)malloc( len ); memcpy( event.buf.data, buffer, len ); memset( rtl8139->receive_buffer, 0, len + 2 * sizeof( uint16_t ) ); } else { // make sure we don't have a runt package puts( "rtl8139: too small frame seen. packet ignored." ); } // adjust buffer pos rtl8139->receive_buffer_pos += 2 * sizeof( uint16_t ); rtl8139->receive_buffer_pos += len; rtl8139->receive_buffer_pos = ( rtl8139->receive_buffer_pos + 3 ) & ~0x3; if( rtl8139->receive_buffer_pos >= RECEIVE_TOTAL_BUFFER_SIZE ) { rtl8139->receive_buffer_pos = 0; } // initial CAPS is set to FFF0, we deduce from this that we have // to subtract 16 from the actual buffer offset (not mentioned in // documentation of the card!) port16_write( &rtl8139->CAPR_port, rtl8139->receive_buffer_pos - 16 ); if( len >= ETHERNET_MIN_FRAME_SIZE_IEEE_802_3 ) { rtl8139->base.handler( &event, rtl8139 ); } } } uint32_t rtl8139_handle_interrupt( interrupt_handler_t *handler, uint32_t esp ) { rtl8139_t *rtl8139 = (rtl8139_t *)handler->driver; uint16_t isr = port16_read( &rtl8139->ISR_port ); uint16_t reset_isr = 0; // TODO: early interrupt line cleaning or later? port16_write( &rtl8139->ISR_port, isr ); #ifdef DEBUG printf( "rtl8139: INTERRUPT: %X\n", isr ); #endif if( isr & ISR_RECEIVE_OK ) { // TODO: manual says, we should reset the ISR bit first, // then go into a read loop? handle_receive_packet( rtl8139 ); } // reset interrupt lines when consumed //~ port16_write( &rtl8139->ISR_port, isr ); return esp; } void rtl8139_print_name( void *obj ) { puts( "Realtek 8139 network card driver" ); } void rtl8139_print_info( void *obj ) { rtl8139_t *rtl8139 = (rtl8139_t *)obj; char buf[NETWORK_ETHERNET_MAC_ADDR_STRING]; char buf2[30]; if( rtl8139->model == NULL ) { snprintf( buf2, 30, "unknown submodel 0x%X", rtl8139->model_id ); } network_mac_address_to_string( rtl8139->base.mac_address, buf, 20 ); printf( "rtl8139 NIC type %s\n at I/O base 0x%X, interrupt 0x%X, MAC: %s\n", ( rtl8139->model != NULL ) ? rtl8139->model : buf2, rtl8139->pci_descriptor.port_base, rtl8139->pci_descriptor.interrupt, buf ); } void rtl8139_write( void *obj, network_buffer_t buf ) { #ifdef DEBUG printf( "rtl8139: sending data of length %d\n", buf.length ); printf( "rtl8139: " ); for( int i = 0; i < buf.length; i++ ) { printf( "%X ", ((const uint8_t *)buf.data)[i] ); } puts( "" ); #endif }