-- (c) William Welch 2004 -- -- This software is provided 'as-is', without any express or implied -- warranty. In no event will the authors be held liable for any damages -- arising from the use of this software. -- -- Permission is granted to anyone to use this software for any purpose, -- including commercial applications, and to alter it and redistribute it -- freely, subject to the following restrictions: -- -- 1. The origin of this software must not be misrepresented; -- you must not claim that you wrote the original software. -- If you use this software in a product, an acknowledgment in -- the product documentation would be appreciated but is not required. -- -- 2. Altered source versions must be plainly marked as such, and must -- not be misrepresented as being the original software. -- -- 3. This notice may not be removed or altered from any source distribution. -- -- This license is commonly known as the zlib/libpng License. -- -- net.jal bvwelch 15 may 2004 -- revised 27 may 2005 -- ethernet-specific routines. "physical layer". -- RFC 1042 Describes how to layer IP and ARP over the IEEE 802.3 ethernet -- References: National Semiconduction Application Notes: 874 and 475 -- and the National DP83901A datasheet. -- Also, Linux ne2000 driver by Donald Becker. -- FIXME we need to test this new layout const byte TX_STARTPAGE = 0x40 const byte RX_STARTPAGE = 0x45 const byte RX_STOPPAGE = 0x5F const byte TELNET_PAGE = 0x60 const byte HTTP_PAGE = 0x65 const byte DMAPORT = 0x10 const byte RESETPORT = 0x1f -- Page 0 ReadOnly Registers const byte TRANSMITSTATUS = 0x04 -- Page 0 WriteOnly Registers const byte PAGESTART = 0x01 const byte PAGESTOP = 0x02 const byte TRANSMITPAGE = 0x04 const byte TRANSMITBYTECOUNT0 = 0x05 const byte TRANSMITBYTECOUNT1 = 0x06 const byte REMOTESTARTADDRESS0 = 0x08 const byte REMOTESTARTADDRESS1 = 0x09 const byte REMOTEBYTECOUNT0 = 0x0A const byte REMOTEBYTECOUNT1 = 0x0B const byte RECEIVECONFIGURATION = 0x0C const byte TRANSMITCONFIGURATION = 0x0D const byte DATACONFIGURATION = 0x0E const byte INTERRUPTMASK = 0x0F -- Page 0 Read/Write Registers const byte COMMAND = 0x00 const byte BOUNDARY = 0x03 const byte INTERRUPTSTATUS = 0x07 -- Page 1 Read/Write Registers const byte CURRENT = 0x07 const byte TCRVAL = 0 const NE2K_TIMER_MAX = 10000 var byte net_http_clk, net_telnet_clk var byte pkt_owner -- increment these variables approximately once per second procedure ne2k_timer is I16_DEC(ne2k_clk) if I16_CEQ ( ne2k_clk , ZERO16 ) == false then return end if I16_LDL ( ne2k_clk , ( NE2K_TIMER_MAX >> 8 ) , ( NE2K_TIMER_MAX & 0xff ) ) net_http_clk = net_http_clk + 1 net_telnet_clk = net_telnet_clk + 1 end procedure -- see app note 874. Also DP83901A datasheet section 11.0 -- TODO document input argument list, loopback mode, etc procedure ne2k_init ( byte in sniffer_mode, byte in tx_enable ) is var byte v const byte DCR = 0x48 const byte IMR = 0x11 I16_LDL ( ne2k_clk , ( NE2K_TIMER_MAX >> 8 ) , ( NE2K_TIMER_MAX & 0xff ) ) -- stop/software reset. select page 0 isa_write(COMMAND, 0x21) delay_100ms( 1 ) isa_write(DATACONFIGURATION, DCR) isa_write(REMOTEBYTECOUNT0, 0) isa_write(REMOTEBYTECOUNT1, 0) if sniffer_mode == 1 then isa_write(RECEIVECONFIGURATION, 0x1C) else isa_write(RECEIVECONFIGURATION, 0x0C) end if isa_write(TRANSMITPAGE, TX_STARTPAGE) isa_write(TRANSMITCONFIGURATION, 0x02) isa_write(PAGESTART, RX_STARTPAGE) isa_write(BOUNDARY, RX_STARTPAGE) isa_write(PAGESTOP, RX_STOPPAGE) isa_write(INTERRUPTSTATUS, 0xff) isa_write(INTERRUPTMASK, IMR) -- stop/software reset. select page 1 isa_write(COMMAND, 0x61) v = MemRdEx ( MYMAC , 0 ) isa_write(1, v) v = MemRdEx ( MYMAC , 1 ) isa_write(2, v) v = MemRdEx ( MYMAC , 2 ) isa_write(3, v) v = MemRdEx ( MYMAC , 3 ) isa_write(4, v) v = MemRdEx ( MYMAC , 4 ) isa_write(5, v) v = MemRdEx ( MYMAC , 5 ) isa_write(6, v) if sniffer_mode == 1 then isa_write(0x8, 0xff) isa_write(0x9, 0xff) isa_write(0xa, 0xff) isa_write(0xb, 0xff) isa_write(0xc, 0xff) isa_write(0xd, 0xff) isa_write(0xe, 0xff) isa_write(0xf, 0xff) end if isa_write(CURRENT, RX_STARTPAGE) -- start/activate. select page 0 isa_write(COMMAND, 0x22) if tx_enable == 1 then isa_write(TRANSMITCONFIGURATION, TCRVAL) end if net_http_clk = 0 net_telnet_clk = 0 pkt_owner = 0 end procedure procedure ne2k_reset_chip is var byte v -- set i/o control signals to a known state isa_addr = 0x00 isa_ior = high isa_iow = high isa_reset = low isa_data_direction = all_input -- assert "ISA reset" delay_100ms isa_reset = high delay_10ms isa_reset = low delay_10ms -- TODO: not sure if this next reset is required, or even correct. v = isa_read(RESETPORT) isa_write(RESETPORT, v) delay_100ms end procedure -- quick and dirty routine. we "know" this routine is only called twice. var byte first_alloc first_alloc = 1 function net_alloc ( byte out fid_hi , byte out fid_lo ) return bit is if first_alloc == 1 then fid_hi = TELNET_PAGE fid_lo = 0 else fid_hi = HTTP_PAGE fid_lo = 0 end if first_alloc = 0 return true end function -- NOTE: ne2k version ignores the "offset" field. Should be OK. procedure net_putbuf ( byte in fid_hi, byte in fid_lo, byte in offset, byte in buf, byte in len_hi , byte in len_lo ) is var byte v isa_write(COMMAND, 0x22) isa_write(REMOTESTARTADDRESS0, fid_lo) isa_write(REMOTESTARTADDRESS1, fid_hi) isa_write(REMOTEBYTECOUNT0, len_lo) isa_write(REMOTEBYTECOUNT1, len_hi) isa_write(COMMAND, 0x12) -- handle large buffers I16_LDL ( P_N16 , len_hi , len_lo ) LoadPointer_2 ( buf ) while I16_CEQ ( P_N16 , ZERO16 ) == false loop v = MemNext2 isa_write(DMAPORT, v) I16_DEC ( P_N16 ) end loop while ( isa_read(INTERRUPTSTATUS) & 0x40 ) == 0 loop asm nop end loop end procedure -- NOTE: ne2k version ignores the "offset" field. Should be OK. procedure net_getbuf ( byte in fid_hi , byte in fid_lo , byte in offset , byte in buf , byte in len_hi , byte in len_lo ) is var byte v isa_write(COMMAND, 0x22) isa_write(REMOTESTARTADDRESS0, fid_lo) isa_write(REMOTESTARTADDRESS1, fid_hi) isa_write(REMOTEBYTECOUNT0, len_lo) isa_write(REMOTEBYTECOUNT1, len_hi) isa_write(COMMAND, 0x0A) -- handle large buffers I16_LDL ( P_N16 , len_hi , len_lo ) LoadPointer_2 ( buf ) while I16_CEQ ( P_N16 , ZERO16 ) == false loop v = isa_read(DMAPORT) MemNext2 = v I16_DEC ( P_N16 ) end loop while ( isa_read(INTERRUPTSTATUS) & 0x40 ) == 0 loop asm nop end loop end procedure -- FIXME: better handling of small packets < 60 bytes procedure net_txpkt ( byte in len_hi, byte in len_lo ) is var byte v if len_hi == 0 then if len_lo < 60 then len_lo = 60 end if end if -- wait if the transmitter is already busy. -- TODO: what about overwrite problem? How about some sort of timeout??? while ( isa_read(COMMAND) & 4 ) != 0 loop asm nop end loop isa_write(COMMAND, 0x22) isa_write(REMOTESTARTADDRESS0, 0) isa_write(REMOTESTARTADDRESS1, TX_STARTPAGE) isa_write(REMOTEBYTECOUNT0, len_lo) isa_write(REMOTEBYTECOUNT1, len_hi) isa_write(COMMAND, 0x12) -- handle large buffers I16_LDL ( P_N16 , len_hi , len_lo ) LoadPointer_2 ( PKT ) while I16_CEQ ( P_N16 , ZERO16 ) == false loop v = MemNext2 isa_write(DMAPORT, v) I16_DEC ( P_N16 ) end loop while ( isa_read(INTERRUPTSTATUS) & 0x40 ) == 0 loop asm nop end loop isa_write(TRANSMITPAGE, TX_STARTPAGE) isa_write(TRANSMITBYTECOUNT0, len_lo) isa_write(TRANSMITBYTECOUNT1, len_hi) isa_write(COMMAND, 0x26) -- TODO: maybe we don't need to wait for this. while isa_read(TRANSMITSTATUS) == 0 loop asm nop end loop end procedure function ne2k_rxpkt return byte is var byte pktstatus, nextptr, pktsize_lo, pktsize_hi, n, v isa_write(COMMAND, 0x1A) pktstatus = isa_read(DMAPORT) nextptr = isa_read(DMAPORT) pktsize_lo = isa_read(DMAPORT) pktsize_hi = isa_read(DMAPORT) -- sanity check. should never happen. if nextptr < RX_STARTPAGE then return 0xff end if if nextptr > RX_STOPPAGE then return 0xff end if if pktsize_hi > 5 then putc = "R" putc = "X" putc = "?" return 0xff end if -- handle large buffers I16_LDL ( P_N16 , pktsize_hi , pktsize_lo ) MemSet ( PKT , 0, 34) LoadPointer_2 ( PKT ) while I16_CEQ ( P_N16 , ZERO16 ) == false loop v = isa_read(DMAPORT) MemNext2 = v I16_DEC ( P_N16 ) end loop -- TODO: is this really necessary ? while ( isa_read(INTERRUPTSTATUS) & 0x40 ) == 0 loop asm nop end loop return 1 end function function net_rxpoll return bit is var byte isr_status, curr, bndry, rxpkt_stat, v pkt_owner = 0 ne2k_timer isr_status = isa_read(INTERRUPTSTATUS) -- Buffer ring overflow. Follows the procedure in section 7.0 of National DP83901A datasheet if ( isr_status & 0x10 ) != 0 then var byte cmd, resend cmd = isa_read(COMMAND) isa_write(COMMAND, 0x21) if debug_mode then putc = "O" putc = "V" putc = "R" putc = " " end if delay_2ms isa_write(REMOTEBYTECOUNT0, 0) isa_write(REMOTEBYTECOUNT1, 0) resend = 0 if (cmd & 4) != 0 then isr_status = isa_read(INTERRUPTSTATUS) if (isr_status & 0x0A) == 0 then resend = 1 end if end if isa_write(TRANSMITCONFIGURATION, 0x02) isa_write(COMMAND, 0x22) if resend == 1 then if debug_mode then putc = "T" end if end if end if if ( isr_status & 1 ) == 1 then isa_write(INTERRUPTSTATUS, 1) end if isa_write(COMMAND, 0x62) curr = isa_read(CURRENT) isa_write(COMMAND, 0x22) bndry = isa_read(BOUNDARY) if (curr == bndry) then return false end if rxpkt_stat = ne2k_rxpkt if rxpkt_stat == 0 then return false end if if rxpkt_stat == 0xff then if debug_mode then putc = "N" putc = "E" putc = "2" putc = "K" putc = "?" putc = "?" end if ne2k_init (0, 1) return false end if -- check ethernet mac dest. broadcasts are OK. If not broadcast, does it match? v = MemRdEx ( PKT , 0 ) if (v & 1) == 0 then if MemCompEx ( PKT , 0 , MYMAC , 0 , 6 ) == false then return false end if end if -- part of the overflow procedure in section 7.0 of National DP83901A datasheet if ( isr_status & 0x10 ) != 0 then isa_write(INTERRUPTSTATUS, 0x10) isa_write(TRANSMITCONFIGURATION, TCRVAL) end if return true end function -- dump ne2000 registers procedure ne2k_regdump is if debug_mode then putc = "r" putc = "e" putc = "g" putc = "s" putc = ":" putc = 13 putc = 10 puthex ( isa_read ( 0x00 ) ) puthex ( isa_read ( 0x01 ) ) puthex ( isa_read ( 0x02 ) ) puthex ( isa_read ( 0x03 ) ) puthex ( isa_read ( 0x04 ) ) puthex ( isa_read ( 0x05 ) ) puthex ( isa_read ( 0x06 ) ) puthex ( isa_read ( 0x07 ) ) puthex ( isa_read ( 0x08 ) ) puthex ( isa_read ( 0x09 ) ) puthex ( isa_read ( 0x0A ) ) puthex ( isa_read ( 0x0B ) ) puthex ( isa_read ( 0x0C ) ) puthex ( isa_read ( 0x0D ) ) puthex ( isa_read ( 0x0E ) ) puthex ( isa_read ( 0x0F ) ) putc = 13 putc = 10 end if end procedure -- generic read from any location in ne2000 RAM. -- Warning: This is a lot of overhead for just a single byte of data, but -- perhaps useful for diagnostics. function net_dmaread(byte in addr_hi, byte in addr_lo) return byte is var byte v isa_write(COMMAND, 0x22) isa_write(REMOTESTARTADDRESS0, addr_lo) isa_write(REMOTESTARTADDRESS1, addr_hi) isa_write(REMOTEBYTECOUNT0, 1) isa_write(REMOTEBYTECOUNT1, 0) isa_write(COMMAND, 0x0A) v = isa_read(DMAPORT) while ( isa_read(INTERRUPTSTATUS) & 0x40 ) == 0 loop asm nop end loop return v end function -- Try some of the "probe" ideas from the Linux driver. -- note: be sure to "reset" the chip again, after this routine is finished. procedure ne2k_probe is var byte n, wlen, a, b var bit nonzero -- dump first 32 bytes of ne2000 SRAM -- if the ne2000 has a prom, the first 6 bytes is the MAC address. -- NOTE: if your board does not have a prom, you may get all zeros -- except maybe some 0x57 which means ne2000 compatible. -- also, check for board accidentally strapped for 16-bit mode. -- we only support 8-bit mode on JAL ethernet, to save I/O pins. -- NOTE: the test for 16-bit versus 8-bit is meaningless if the prom -- is missing, so don't worry if you see "pr0", but otherwise your -- board is working OK. if debug_mode then putc = "r" putc = "a" putc = "m" putc = ":" putc = 13 putc = 10 wlen = 2 n = 0 nonzero = false for 16 loop a = net_dmaread(0, n) puthex(a) n = n + 1 b = net_dmaread(0, n) puthex(b) n = n + 1 if a != b then wlen = 1 end if if (a != 0) | (b != 0) then nonzero = true end if if n == 16 then putc = 13 putc = 10 end if end loop putc = 13 putc = 10 if nonzero & (wlen == 2) then putc = "p" putc = "r" putc = "0" putc = 13 putc = 10 end if -- write to location 0xD in "page 1", then reset and read-back the "tally" -- counter in "page 0". If we don't have a ne2000 compatible chip, this will fail. isa_write(COMMAND, 0x61) isa_write(0xd, 0xff) isa_write(COMMAND, 0x21) -- this read "resets" the tally counter n = isa_read(0xd) -- read it again, should be zero if isa_read(0xd) != 0 then -- this is bad news. wrong chip, wiring error, something??? putc = "p" putc = "r" putc = "1" putc = 13 putc = 10 end if -- if realtek chip, it has a couple of proprietary registers with known values. if ( isa_read(0xa) == 0x50 ) & ( isa_read(0xb) == 0x70 ) then putc = "R" putc = "T" putc = "L" putc = "8" putc = "0" putc = "1" putc = "9" putc = 13 putc = 10 end if end if end procedure procedure net_init is ne2k_reset_chip ne2k_init ( 0, 0) ne2k_regdump ne2k_probe ne2k_reset_chip ne2k_init (0, 1) end procedure