elks-enhanced
public
Read
Owner: themaster
Branch: master
Commits: 6893
Updated: 2026-04-19 00:15
Git CLI clone URL
git clone https://www.xt-emporium.com/git/elks-enhanced.git
Fullscreen desktop URL
Code
Commits
History
Branches
Bug Reports
Discussions
Compare
Settings
elks-enhanced
/
elks
/
arch
/
i86
/
drivers
/
net
/
ne2k-asm.S
File editor
//----------------------------------------------------------------------------- // NE2K driver - low part - MAC routines // // Updated by Helge Skrivervik (HS) July 2020: // . pick up MAC address from prom // . fixed read ring buffer wrap around errors // . added ring buffer overflow handling // . use word I/O // oct-2021: Pick up I/O port # from init/bootopts (HS) // Updated by Santiago Hormazabal on Dec 2021: // . 8 bit access if CONFIG_ETH_BYTE_ACCESS is set, using a ne1k patch from // NCommander. // apr-2022 (HS) : Support auto detection of 8bit mode, set 8k buffer size when in 8bit mode. // Rewrote overflow handler, added direct access to many registers from C code // Cleaned up initialization code, optimized 8bit I/O // //----------------------------------------------------------------------------- // Terminology // The ring buffer pointer terminology used in the DP8390C/NS3249C document is confusing. // 'CURRENT' points to the next block to be filled by an incoming packet // 'BOUNDARY' points to the next block to be read from the NIC // The two must not be equal. If the ring buffer is empty, BOUNDARY = CURRENT -1 // So the next block to read is not BOUNDARY but BOUNDARY + 1. Easy - except at // the wrap-around point: BOUNDARY may be 80 (60 if 8bit), while the next block to read is 46. // For simplicity (to avoid the ring wraparound logic on every read) // the driver uses a variable to hold the next block to read - ne2k_next_pk. // For debugging, this variable is also available to the C part of the driver. // //----------------------------------------------------------------------------- //#include <linuxmt/config.h> //#include "arch/ports.h" #include <arch/asm-offsets.h> #include <linuxmt/netstat.h> .code16 // register array - offset from base I/O address io_ne2k_command = 0x00 // command register at base address io_ne2k_rx_first = 0x01 // page 0 io_ne2k_rx_last = 0x02 // page 0 io_ne2k_rx_get = 0x03 // page 0 io_ne2k_tx_start = 0x04 // page 0 - write io_ne2k_tx_len1 = 0x05 // page 0 - write io_ne2k_tx_len2 = 0x06 // page 0 - write io_ne2k_int_stat = 0x07 // page 0 io_ne2k_dma_addr1 = 0x08 // page 0 io_ne2k_dma_addr2 = 0x09 // page 0 io_ne2k_dma_len1 = 0x0A // page 0 - write io_ne2k_dma_len2 = 0x0B // page 0 - write io_ne2k_rx_stat = 0x0C // page 0 - read io_ne2k_tx_stat = 0x04 // page 0 - read io_ne2k_rx_conf = 0x0C // page 0 - write io_ne2k_tx_conf = 0x0D // page 0 - write io_ne2k_data_conf = 0x0E // page 0 - write io_ne2k_int_mask = 0x0F // page 0 - write io_ne2k_frame_errs = 0x0D // page 0 read - Frame Alignment Error counter io_ne2k_crc_errs = 0x0E // page 0 read - CRC error counter io_ne2k_lost_pkts = 0x0F // page 0 read - Lost packet counter io_ne2k_unicast = 0x01 // page 1 - 6 bytes io_ne2k_rx_put = 0x07 // page 1 io_ne2k_multicast = 0x08 // page 1 - 8 bytes io_ne2k_data_io = 0x10 // 2 bytes io_ne2k_reset = 0x1F // Really a port, not a register, force HW reset of the chip // Ring segmentation tx_first = 0x40 rx_first = 0x46 rx_last_16 = 0x80 rx_last_8 = 0x60 // For 8k buffer in 8 bit mode - per spec. // Flags BUF_4K = ETHF_4K_BUF BUF_8K = ETHF_8K_BUF BUF_16K = ETHF_16K_BUF BUF_FLAGS = (ETHF_4K_BUF|ETHF_8K_BUF|ETHF_16K_BUF) ISA_8B = ETHF_8BIT_BUS ISA_16B = ETHF_16BIT_BUS USE_AUI = ETHF_USE_AUI //----------------------------------------------------------------------------- .data .extern current .extern net_port // io-port base .global ne2k_next_pk ne2k_next_pk: .word 0 // being used as byte ... .global ne2k_has_data ne2k_has_data: .word 0 .global ne2k_flags // flags ne2k_flags: .word 0 ne2k_rx_last: // PSTOP - buffer upper bound .byte rx_last_16 // default to 16K .text //----------------------------------------------------------------------------- // Set unicast address (aka MAC address) //----------------------------------------------------------------------------- // arg1 : pointer to unicast address (6 bytes) .global ne2k_addr_set ne2k_addr_set: push %bp mov %sp,%bp push %si mov 4(%bp),%si mov net_port,%dx // command-register mov $0x42,%al // page 1 out %al,%dx // load MAC address mov net_port,%dx add $io_ne2k_unicast,%dx mov $6,%cx cld ems_loop: lodsb out %al,%dx inc %dx loop ems_loop mov net_port,%dx // command register mov $0x02,%al // back to pg 0 out %al,%dx pop %si pop %bp ret //----------------------------------------------------------------------------- // DMA initialization - Prepare for internal NIC DMA transfer //----------------------------------------------------------------------------- // Uses: DX, AX // BX : chip memory address (4000h...8000h) // CX : byte count dma_init: // set DMA start address mov net_port,%dx add $io_ne2k_dma_addr1,%dx mov %bl,%al out %al,%dx inc %dx // io_ne2k_dma_addr2 mov %bh,%al out %al,%dx // set DMA byte count inc %dx // io_ne2k_dma_len1 mov %cl,%al out %al,%dx inc %dx // io_ne2k_dma_len2 mov %ch,%al out %al,%dx ret //----------------------------------------------------------------------------- // Write data block to NIC with internal DMA //----------------------------------------------------------------------------- // // BX : NIC memory address (to write to) // CX : byte count // DS:SI : host memory address (to read from) //------------------------------------- dma_write: push %cx push %bx // TODO check if this is required (2) push %ds inc %cx // make byte count even and $0xfffe,%cx cli // Mandatory call dma_init // start DMA write mov net_port,%dx // command register mov $0x12,%al out %al,%dx // I/O write loop //mov net_port,%dx add $io_ne2k_data_io,%dx mov ne2k_flags,%ax // Get this before changing the data segment mov current,%bx // setup for far memory xfer mov TASK_USER_DS(%bx),%ds cld test $ISA_8B,%ax // checking ne2k_flags jz wr_loop_w // Byte loop wr_loop_b: lodsb outb %al,%dx loop wr_loop_b jmp wr_loop_done // word loop wr_loop_w: shr %cx 3: lodsw out %ax,%dx loop 3b wr_loop_done: pop %ds // get the data segment back // otherwise the net_port reference below won't work // wait for DMA completed check_dma_w: mov net_port,%dx add $io_ne2k_int_stat,%dx in %dx,%al and $0x40,%al // make sure we're done jz check_dma_w //mov $0x40,%al // clear DMA intr bit in ISR out %al,%dx sti // Mandatory pop %bx pop %cx ret //----------------------------------------------------------------------------- // Read data block from chip with internal DMA //----------------------------------------------------------------------------- // // BX : NIC memory to read from // CX : byte count // ES:DI : host memory to write to // AL: : 0: buffer is local (kernel), <>0: buffer is far (process) dma_read: push %di push %es push %bx pushfw // save interrupt state push %ax inc %cx // make byte count even and $0xfffe,%cx cli // Experimental - disable INTR call dma_init mov %ds,%bx mov %bx,%es pop %ax cmp $0,%al // Use local buffer if zero jz buf_local mov current,%bx // Normal: read directly into the (far) buffer mov TASK_USER_DS(%bx),%es buf_local: mov net_port,%dx // command register mov $0x0a,%al // set RD0 & STA out %al,%dx // start DMA read //mov net_port,%dx // Already in DX add $io_ne2k_data_io,%dx cld // clear direction flag testw $ISA_8B,ne2k_flags jz word_loop0 byte_loop: inb %dx,%al stosb loop byte_loop jmp 3f // Word transfer word_loop0: shr %cx // half -> word size transf word_loop: in %dx,%ax stosw loop word_loop 3: mov net_port,%dx add $io_ne2k_int_stat,%dx check_dma_r: in %dx,%al test $0x40,%al // dma done? jz check_dma_r mov $0x40,%al // clear ISR (RDC bit only) out %al,%dx popfw //Experimental - reenable interrupt state pop %bx pop %es pop %di ret // //----------------------------------------------------------------------- // ne2k_getpage -- return ring buffer page numbers in AX: // AH = CURRENT - where the next received packet will be stored, // AL = BOUNDARY - where the next read from the buffer will start //----------------------------------------------------------------------- // NOTE: BOUNDARY is always one behind where the next read will start, the real // read point is in ne2k_next_pk. This trick is necessary // because the internal logic in the NIC will trigger an overrun interrupt // if the BOUNDARY pointer matches or exceeds the CURRENT pointer. //--------------- // Used internally, exposed externally for debugging purposes. // .global ne2k_getpage ne2k_getpage: mov $0x42,%al // page 1 mov net_port,%dx // command register out %al,%dx //mov net_port,%dx // already loaded add $io_ne2k_rx_put,%dx // CURRENT in %dx,%al mov %al,%ah mov $0x02,%al // page 0 mov net_port,%dx // command register out %al,%dx //mov net_port,%dx add $io_ne2k_rx_get,%dx // BOUNDARY in %dx,%al ret //----------------------------------------------------------------------------- // Get RX status //----------------------------------------------------------------------------- // Returns: // AX: status // 01h = Data available in NIC ring buffer .global ne2k_rx_stat ne2k_rx_stat: // get RX put pointer #if 0 mov $0x42,%al // page 1 mov net_port,%dx // command register out %al,%dx //mov net_port,%dx add $io_ne2k_rx_put,%dx in %dx,%al mov %al,%ah mov $0x02,%al // back to page 0 mov net_port,%dx // command register out %al,%dx // get RX get pointer mov ne2k_next_pk,%al cmp %al,%ah // The ring is empty if they are equal. jz nrs_empty cmp $0,%ax jz nrs_empty mov $1,%ax // Yes, we have data jmp nrs_exit nrs_empty: xor %ax,%ax nrs_exit: #else // sep2020: keep ring buffer status in a variable // instead of accessing the NIC registers continuously. movw ne2k_has_data,%ax #endif ret //----------------------------------------------------------------------------- // Get received packet //----------------------------------------------------------------------------- // arg1: buffer to receive the data // arg2: int - requested read size (max buffer) // arg3: int array [2] (return) containing the NIC packet header. // // returns: // AX : < 0 if error, >0 is length read .global ne2k_pack_get ne2k_pack_get: push %bp mov %sp,%bp push %di //push %es //sub $4,%sp // temp space //mov %sp,%di // get the 4 byte header first -> arg3 mov 8(%bp),%di mov ne2k_next_pk,%bh xor %bl,%bl // Next pkt to read in BX mov $4,%cx // Bytes to read //mov %ds,%ax //mov %ax,%es // local address space xor %al,%al // indicate local address space call dma_read mov 0(%di),%ax // AH : next record, AL : status mov 2(%di),%cx // packet size (without CRC) // get the actual data //add $4,%sp mov 4(%bp),%di // Buffer address to receive data. mov 6(%bp),%dx // read len cmp %cx,%dx // choose the shorter jnb npg_cont0 mov %dx,%cx #if 0 // ------------------------------------------------------------- // Packet size check not required since the NIC will not // accept such packets per our initialization. Note, in order to handle // erroneous packets, rx_get (BOUNDARY) pointer must be updated // to point to the next packet. // If oversized packets still occur, it's a driver problem (most likely // reading the wrong buffer page). // ------------------------------------------------------------- or %cx,%cx // zero length jz npg_err2 cmp $1528,%cx // max - head - crc jnc npg_err #endif // ------------------------------------------------------------- // This section did the smart thing when reading the NIC packet header: // Got the entire block (256b) instead of the 4 first bytes. Which was great // for small packets (telnet, command packets etc.): One read instead of 2. // // Removed when read was changed to put data directly into process address space, // there is no longer anywhere to put the first 4 bytes. //sub $252,%cx // Got entire packet? //jle npg_cont // If not, get rest. //inc %bh // Point to next page //cmp $rx_last,%bh // check wraparound //jnz npg_cont0 //mov $rx_first,%bh npg_cont0: //add $256,%di // Update destination memory address // (keep the 4 byte NIC header) push %cx // save length push %ax add $4,%bx // Skip the 4 bytes already read mov $1,%al // use far transfer call dma_read pop %ax // update RX_get pointer (BOUNDARY, end of ring) npg_cont: xchg %al,%ah // get pointer to %al mov %al,ne2k_next_pk // save 'real' next ptr mov %al,%bl // save for later dec %al cmp $rx_first,%al jnb npg_next // if the decrement sent us outside the ring.. mov ne2k_rx_last,%al dec %al npg_next: mov net_port,%dx add $io_ne2k_rx_get,%dx // update RX_get (BOUNDARY) out %al,%dx npg_exit: // This is effectively the replacement for the rx_stat routine, // clear the has_data flag if ring buffer is empty. cli // Ensure we don't get a race condition when // updating ne2k_has_data call ne2k_getpage cmp %ah,%bl // ring buffer empty? jnz npg_exit_ok movw $0,ne2k_has_data npg_exit_ok: sti // Enable interrupts pop %ax // return byte count (from %cx) //pop %es pop %di //mov %bp,%sp // restore stack pointer pop %bp ret //----------------------------------------------------------------------------- // Get TX status: Read the TXP bit in the command reg, if reset, there is no // transmit in progress. //----------------------------------------------------------------------------- // returns: // AX: // 02h = ready to send .global ne2k_tx_stat ne2k_tx_stat: mov net_port,%dx // command register in %dx,%al test $0x04,%al jz nts_ready xor %ax,%ax jmp nts_exit nts_ready: mov $2,%ax nts_exit: ret //----------------------------------------------------------------------------- // Send packet: First transfer packet data to NIC memory, then kick off // the actual transmit and return. //----------------------------------------------------------------------------- // arg1 : packet buffer to transfer // arg2 : size in bytes // returns: // AX : error code .global ne2k_pack_put ne2k_pack_put: push %bp mov %sp,%bp push %si // write packet to chip memory mov 6(%bp),%cx // arg2 - count xor %bl,%bl mov $tx_first,%bh mov 4(%bp),%si // arg1 - buffer call dma_write // copy the data // set TX pointer and length mov net_port,%dx add $io_ne2k_tx_len1,%dx mov %cl,%al out %al,%dx inc %dx // = io_ne2k_tx_len2 mov %ch,%al out %al,%dx // start TX tx_rdy_wait: mov net_port,%dx // command register in %dx,%al test $0x4,%al // Check that previous transmit completed. // If we skip the completion test below // (let the transmit run on its own for // efficiency) this test makes sense. OTOH // we're doing the same test in write and select // so objectively it's superfluous. jnz tx_rdy_wait //and $0x18,%al // Don't do this, it will set RD2 and //or $6,%al // cause an extra RDC abort interrupt mov $6,%al // set TX + STA out %al,%dx // start transfer #if 0 // EXPERIMENTAL - do we want to wait for completion here? // If we wait, we may as well check error status // and repeat the send if it failed (while we have the data at hand). 1: mov net_port,%dx // command register in %dx,%al test $4,%al // Wait for completion jnz 1b #endif xor %ax, %ax // Always zero return pop %si pop %bp ret //----------------------------------------------------------------------------- // Get NE2K interrupt status //----------------------------------------------------------------------------- // returns interrupt status reg unmodified // AX : status // 01h = packet received // 02h = packet sent // 10h = RX ring overflow // 40h = Remote DMA complete .global ne2k_int_stat ne2k_int_stat: mov net_port,%dx add $io_ne2k_int_stat,%dx in %dx,%al xor %ah,%ah ret //-------------------------------------------------------------- // Initialization operations common to several internal routines // Uses: AX, DX //-------------------------------------------------------------- ne2k_base_init: mov net_port,%dx // command register mov $0x21,%al // page 0 + Abort DMA; STOP out %al,%dx // Set data size, 16 or 8 bits // Some machines are 8 bit only, // some interfaces are 8 bits only //mov net_port,%dx add $io_ne2k_data_conf,%dx mov $0x49,%al // set word access testw $ISA_8B,ne2k_flags jz 1f dec %al // if in 8bit mode, // set byte access, data_conf reg = 0x48 1: out %al,%dx // clear DMA length xor %al,%al mov net_port,%dx add $io_ne2k_dma_len1,%dx out %al,%dx inc %dx // = io_ne2k_dma_len2 out %al,%dx ret //----------------------------------------------------------------------------- // NE2K initialization // Called from device open // Uses AX, DX only //----------------------------------------------------------------------------- .global ne2k_init ne2k_init: call ne2k_base_init // basic initialization // Accept only packets without errors. // Unicast & broadcast, no promiscuous, no multicast mov net_port,%dx add $io_ne2k_rx_conf,%dx mov $0x04,%al out %al,%dx // half-duplex and internal loopback // to insulate the MAC while stopped. mov net_port,%dx add $io_ne2k_tx_conf,%dx mov $2,%al // 2 for loopback out %al,%dx // set RX ring limits - 16KB on-chip memory // less one TX frame at the beginning (6 x 256B). // The defaults are 16k if 16 bit NIC, 8k if 8bit NIC. // Flags may force other sizes (up to 32k), no // sanity checking is done. See flags in netstat.h mov net_port,%dx add $io_ne2k_rx_first,%dx mov $rx_first,%al // start of ring, usually 0x46 out %al,%dx // set ending page for the ring buffer, // check for forced buffer size movw ne2k_flags,%ax and $BUF_FLAGS,%al // Check for forced buffer size jz 2f movb $0x10,%ah // 4k minimum push %cx // FIXME: Is this required?? and $3,%al // remove the BUF_4K bit (0x04) from flags // keep (BUF_8K|BUF_16K) mov %al,%cl shl %cl,%ah // %AL is the block count to add to // the buffer start address (usually 0x40), // the sum becoming the PSTOP value add $tx_first,%ah // NOTE: tx_first is where the buffer starts, // we don't want rx_first here. pop %cx mov %ah,%al jmp 1f // The normal case, use defaults 2: movb $rx_last_16,%al testw $ISA_8B,ne2k_flags jz 1f movb $rx_last_8,%al // It's an 8 bit NIC, use 8K buffer 1: movb %al,ne2k_rx_last mov net_port,%dx add $io_ne2k_rx_last,%dx out %al,%dx call ne2k_rx_init // initialize receive buffer // initialize start of TX buffer mov net_port,%dx add $io_ne2k_tx_start,%dx mov $tx_first,%al out %al,%dx // FIXME _ wait till open (start) before enabling intr // set interrupt mask mov net_port,%dx add $io_ne2k_int_mask,%dx // Create and set the effective interrupt mask. // // NOTE: Don't enable RXE intr if running QEMU. // Apparently there is a bug in QEMU which cause continuous interrupts // (and status reg = 00) if RXE is enabled. // OTOH RXE should be enabled if running an 8bit interface. mov $0x1f,%al // 0x53 = RDC, Overflow, RX, TX // 0x13 = Overflow, RX, TX // 0x1F = Overflow, TXE, RXE, RX, TX testw $ISA_8B,ne2k_flags jnz 1f and $0xfb,%al // RXE intr is useful (mostly) if 8bit interface, // clear if 16bit. // That way we get around the QEMU RXE bug too. 1: out %al,%dx // NOTE: The transmitter is not yet enabled, done in the _start routine. // FIXME: Should move the int mask and status reg clearing to // the _start routine too to avoid interrupts from a closed device. ret //------------------------------------------------------------------------ // rx_init // reset the ring buffer front and end pointers to initial values //------------------------------------------------------------------------ .global ne2k_rx_init ne2k_rx_init: // set RX_get pointer [BOUNDARY] mov $rx_first,%al mov %al,%ah // save copy mov net_port,%dx add $io_ne2k_rx_get,%dx out %al,%dx // Ring buffer starting point mov $0x40,%al // Switch to register page 1 mov net_port,%dx // command register out %al,%dx // set RX_put pointer [CURRENT] = RX_get [BOUNDARY] //mov net_port,%dx add $io_ne2k_rx_put,%dx mov %ah,%al // restore $rx_first value inc %al // Keep CURRENT one ahead of BOUNDARY out %al,%dx mov %al,ne2k_next_pk // Initialize our local copy of // BOUNDARY + 1 xor %ax,%ax // Switch back to page 0, don't touch the other bits mov net_port,%dx // command register out %al,%dx movw %ax,ne2k_has_data // ZERO - Insurance, no data available ret //----------------------------------------------------------------------------- // NE2K startup //----------------------------------------------------------------------------- .global ne2k_start ne2k_start: // start the transceiver mov net_port,%dx // command register mov $0x22,%al // ensure page 0 out %al,%dx // move out of internal loopback //mov net_port,%dx add $io_ne2k_tx_conf,%dx xor %al,%al out %al,%dx // FIXME: Move setting the int mask here (from init) ret //----------------------------------------------------------------------------- // NE2K stop //----------------------------------------------------------------------------- .global ne2k_stop ne2k_stop: // Stop the DMA and the MAC mov net_port,%dx // command register mov $0x21,%al // page 0 + stop out %al,%dx // mask all interrrupts add $io_ne2k_int_mask,%dx xor %al,%al out %al,%dx // half-duplex and internal loopback // to insulate the MAC while stopped // and ensure TX finally ends mov net_port,%dx add $io_ne2k_tx_conf,%dx mov $2,%al out %al,%dx // clear DMA length xor %al,%al mov net_port,%dx add $io_ne2k_dma_len1,%dx out %al,%dx inc %dx // = io_ne2k_dma_len2 out %al,%dx // TODO: wait for the chip to get stable???? ret #if 0 /* ne2k_probe in ne2k.c */ //----------------------------------------------------------------------------- // NE2K probe //----------------------------------------------------------------------------- // // Access the command register, check that the changes stick // returns: // AX: 0=found 1=not found .global ne2k_probe ne2k_probe: // Poke then peek at the base address of the interface. // If something is there, return 0. // No attempt is made to get details about the i/f. mov net_port,%dx // command register mov $0x20,%al // set page 0 out %al,%dx in %dx,%al cmp $0xff,%al // cannot be FF jz np_err cmp $0,%al // cannot be 0 jz np_err xor %ax,%ax jmp np_exit np_err: mov $1,%ax np_exit: ret #endif //----------------------------------------------------------------------------- // NE2K reset //----------------------------------------------------------------------------- .global ne2k_reset ne2k_reset: // reset device with pulse on reset port mov net_port,%dx add $io_ne2k_reset,%dx in %dx,%al out %al,%dx mov net_port,%dx add $io_ne2k_int_stat,%dx nr_loop: // wait for reset // without too much CPU hlt in %dx,%al test $0x80,%al // Wait for RST bit to set jz nr_loop // Leave the NIC in a known (stopped) state mov net_port,%dx // command register mov $0x21,%al out %al,%dx ret //----------------------------------------------------------------------------- // Get MAC address from NIC's prom // WARNING: This function will reset the controller. Use before the init()! //----------------------------------------------------------------------------- // arg1 : pointer to 32 bytes buffer .global ne2k_get_hw_addr ne2k_get_hw_addr: push %bp mov %sp,%bp push %di mov 4(%bp),%di // Effectively a soft reset of the NIC, required in order to get access to the // address PROM. The PROM is 16 bytes, we get 32 back if reading in word mode, // the upper byte of each word is garbage. The MAC address is in the first 6 bytes. // The remaining 10 bytes sometimes identify the card type. The PROM content from // an 8 bit Weird Electronics (RTL8019AS) card looks like this: // 001f1102602d49534138455448204242, the last 10 bytes being 'ISA8ETH BB'. // Many 16 bit cards have 0x57 in the last 2 bytes, supposedly indicating // 'true ne2k clones'. w_reset: call ne2k_base_init // basic initialization xor %al,%al mov net_port,%dx add $io_ne2k_int_mask,%dx out %al,%dx // mask all interrupts call ne2k_clr_int_reg// required mov net_port,%dx add $io_ne2k_rx_conf,%dx mov $0x20,%al out %al,%dx // set to monitor mode inc %dx // $io_ne2k_tx_conf mov $2,%al out %al,%dx // Loopback mode // Now read the PROM mov $32,%cx // bytes to read xor %bx,%bx // read from 0:0 xor %al,%al // AL = 0 : local xfer call dma_read mov net_port,%dx add $io_ne2k_tx_conf,%dx // set tx back to normal xor %al,%al out %al,%dx pop %di pop %bp ret //----------------------------------------------------------------------------- // NE2K clear overflow --- respond to an input ring buffer overflow interrupt //----------------------------------------------------------------------------- // input: arg1 = buffer recovery strategy // 0 -> clear input buffer and reset the NIC // 1 -> keep the oldest (next-to-read) packet, always safe // 2 and higher: delete this # of packets from the end (BOUNDARY) // towards the head. In 8bit/4k mode, >1 doesn't make much sense. // The defaults are set in the _intr routine in the .c part of the driver, // typically 3 if the buffer is 16k, 1 or 0 if lower. // // Returns: AL = BOUNDARY ptr, AH = CURRENT ptr for debugging // .global ne2k_clr_oflow ne2k_clr_oflow: push %di push %bp mov %sp,%bp of_cont_1: sub $4,%sp // get temp space on the stack mov %sp,%di // for the dma_read call mov 6(%bp),%bx // arg1, # of packets to kill // We have not cleared the OFLW INT bit yet, so NIC // interrupts are not enabled mov net_port,%dx // command register #if 0 // Should not be required - the STOP command will wait out // whatever is in progess 1: in %dx,%al test $0x4,%al // wait if transmit in progress jnz 1b #endif mov $0x21,%al // page 0 + Abort DMA; STOP out %al,%dx // clear dma counters, required for the STOP command to complete // (and set the RST bit) add $io_ne2k_dma_len1,%dx // io_ne2k_dma_len1 xor %al,%al out %al,%dx inc %dx // io_ne2k_dma_len2 out %al,%dx mov net_port,%dx add $io_ne2k_int_stat,%dx of_reset_wait: in %dx,%al // wait for reset to complete test $0x80,%al jz of_reset_wait mov net_port,%dx add $io_ne2k_tx_conf,%dx // set tx to loopback mov $2,%al out %al,%dx mov net_port,%dx // Command register mov $0x22,%al // Restart NIC out %al,%dx // NIC is running but offline, start deleting of_drop_packets: // initial housekeeping mov ne2k_next_pk,%ah // The 'real' BOUNDARY ptr and $0x7,%bx // limit the drop count and check for ZERO jnz of_drop_loop1 // Drop count is ZERO, purge the buffer call ne2k_rx_init // EXPERIMENTAL - reset the NIC completely // Need more testing to see if this is useful //call ne2k_reset // THE HARD WAY //call ne2k_init //call ne2k_start // enable transmitter jmp of_drop_ok // ... and exit of_drop_loop1: push %bx xor %bl,%bl mov %ah,%bh // Start of next pkt // get header of next packet from ring buffer mov $4,%cx // 4 bytes only xor %al,%al // use local memory call dma_read // remember: interrupts are disabled in dma_read!! mov 0(%di),%ax // AH : next record, AL : status pop %bx // packet counter dec %bx jnz of_drop_loop1 // BX = 1-7 of_drop_1: // Move the front of the ring (CURRENT) to the block # in AH // effectively deleting the rest of the ring. mov net_port,%dx // Command register mov $0x42,%al // set page 1 out %al,%dx //mov net_port,%dx add $io_ne2k_rx_put,%dx mov %ah,%al // set CURRENT to the beginning of the next pkt, out %al,%dx // effectively clearing the rest of the ring. mov net_port,%dx // Command register mov $02,%al // Back to page 0 out %al,%dx #if NOT_SO_SMART jmp of_drop_ok of_drop_2: // ALT 2 (BX = 2), delete this packet, keep the rest - // by moving the BOUNDARY pointer to the beginning of the next packet // May not be safe for 8bit interfaces, the 'next' (last) packet // may be garbage. Experimental - KEPT FOR REFERENCE // Verdict: Does not work well for any setting. //mov %cl,%al // save BOUNDARY for return //push %ax mov net_port,%dx add $io_ne2k_rx_get,%dx mov %ah,%al // set BOUNDARY to the beginning of the next pkt, // don't touch CURRENT mov %al,ne2k_next_pk // do the wrap-around exercise dec %al cmp $rx_first,%al jnb 1f mov ne2k_rx_last,%al dec %al 1: out %al,%dx mov %ch,%ah // return value jmp of_drop_ok #endif of_drop_ok: // insurance: check if the ring buffer is empty. // we may have removed all packets in the buffer (if 4k buffer) call ne2k_getpage push %ax // save for return cmp ne2k_next_pk,%ah // next_pk == CURRENT? jz of_exit0 movw $1,ne2k_has_data jmp of_exit1 of_exit0: movw $0,ne2k_has_data of_exit1: mov net_port,%dx // set tx back to normal add $io_ne2k_tx_conf,%dx xor %al,%al out %al,%dx call ne2k_clr_int_reg // clear all interrupt bits pop %ax // return value from getpage() // (for debugging) of_exit: mov %bp,%sp pop %bp pop %di ret //----------------------------------------------------------------------------- // NE2K Remote DMA complete - for now just a placeholder - // and the right place to reset the intr status bit. //----------------------------------------------------------------------------- .global ne2k_rdc ne2k_rdc: mov net_port,%dx add $io_ne2k_int_stat,%dx // reset the interrupt bit mov $0x40,%al out %al,%dx ret //----------------------------------------------------------------------------- // NE2K get error statistics // returns 3 bytes in the byte_t array[3] pointed to by arg1. // Max value in each counter is 192. After reading, the regs are reset. //----------------------------------------------------------------------------- .global ne2k_get_errstat ne2k_get_errstat: // Currently useful only 4 debugging: Needs a regime to regularly collect // and accumulate the numbers in order to be of statistical value. #if LATER push %bp mov %si,%bp push %di mov 4(%bp),%di // assume pg 0 mov net_port,%dx add $io_ne2k_frame_errs,%dx in %dx,%al stosb inc %dx // $io_ne2k_crc_errs in %dx,%al stosb inc %dx // $io_ne2k_lost_pkts in %dx,%al stosb pop %di pop %bp #endif xor %ax,%ax ret //--------------------------------------------------------------------------- // Ne2k - get TX error status // return the content of the TX status register in AX //--------------------------------------------------------------------------- .global ne2k_get_tx_stat ne2k_get_tx_stat: mov net_port,%dx add $io_ne2k_int_stat,%dx mov $0x0a,%al // Clear PTX & TXE bits in ISR out %al,%dx mov net_port,%dx add $io_ne2k_tx_stat,%dx in %dx,%al xor %ah,%ah ret //--------------------------------------------------------------------------- // Ne2k - get RX error status // return the content of the RX status register in AX //--------------------------------------------------------------------------- .global ne2k_get_rx_stat ne2k_get_rx_stat: // called from interrupt RX RDY processing, must reset the // mov net_port,%dx add $io_ne2k_int_stat,%dx mov $1,%al // Clear PRX bit in ISR out %al,%dx mov net_port,%dx add $io_ne2k_rx_stat,%dx in %dx,%al xor %ah,%ah ret //-------------------------------------------------------------------------- // Ne2k - clear interrupt status reg //-------------------------------------------------------------------------- ne2k_clr_int_reg: mov net_port,%dx add $io_ne2k_int_stat,%dx //in %dx,%al mov $0x7f,%al out %al,%dx ret //-------------------------------------------------------------------------- // Ne2k - clear tally counters // Just read the registers to clear them //-------------------------------------------------------------------------- .global ne2k_clr_err_cnt ne2k_clr_err_cnt: mov net_port,%dx add $io_ne2k_int_stat,%dx mov $0x20,%al out %al,%dx mov net_port,%dx add $io_ne2k_frame_errs,%dx in %dx,%al inc %dx // CRC errors in %dx,%al inc %dx // Missed packets in %dx,%al ret //-----------------------------------------------------------------------------
Commit message
This repository is read-only for this account.
Repository snapshot
Current branch
master
Visibility
public
Your access
Read
Remote
Configured
File activity
View file history