kolibrios-gitea/drivers/ethernet/3c59x.asm

2917 lines
87 KiB
NASM
Raw Normal View History

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2021. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; 3Com network driver for KolibriOS ;;
;; ;;
;; Ported to KolibriOS net-branch by hidnplayr ;;
;; ;;
;; Thanks to: scrap metal recyclers, whom provide me with ;;
;; loads of hardware ;;
;; diamond: who makes me understand KolibriOS ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Original copyright from menuetos driver:
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; 3C59X.INC ;;
;; ;;
;; Ethernet driver for Menuet OS ;;
;; ;;
;; Driver for 3Com fast etherlink 3c59x and ;;
;; etherlink XL 3c900 and 3c905 cards ;;
;; References: ;;
;; www.3Com.com - data sheets ;;
;; DP83840A.pdf - ethernet physical layer ;;
;; 3c59x.c - linux driver ;;
;; ethernet driver template by Mike Hibbett ;;
;; ;;
;; Credits ;;
;; Mike Hibbett, ;;
;; who kindly supplied me with a 3Com905C-TX-M card ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Copyright (c) 2004, Endre Kozma <endre.kozma@axelero.hu>
;; All rights reserved.
;;
;; Redistribution and use in source and binary forms, with or without
;; modification, are permitted provided that the following conditions are
;; met:
;;
;; 1. Redistributions of source code must retain the above copyright notice,
;; this list of conditions and the following disclaimer.
;;
;; 2. Redistributions in binary form must reproduce the above copyright
;; notice, this list of conditions and the following disclaimer in the
;; documentation and/or other materials provided with the distribution.
;;
;; 3. The name of the author may not be used to endorse or promote products
;; derived from this software without specific prior written permission.
;;
;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
;; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;;
;; History
;; =======
;; $Log: 3C59X.INC,v $
;; Revision 1.3 2004/07/11 12:21:12 kozma
;; Support of vortex chips (3c59x) added.
;; Support of 3c920 and 3c982 added.
;; Corrections.
;;
;; Revision 1.2 2004/06/12 19:40:20 kozma
;; Function e3c59x_set_available_media added in order to set
;; the default media in case auto detection finds no valid link.
;; Incorrect mii check removed (3c900 Cyclone works now).
;; Cleanups.
;;
;; Revision 1.1 2004/06/12 18:27:15 kozma
;; Initial revision
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
format PE DLL native
entry START
CURRENT_API = 0x0200
COMPATIBLE_API = 0x0100
API_VERSION = (COMPATIBLE_API shl 16) + CURRENT_API
; configureable area
MAX_DEVICES = 16 ; Maximum number of devices this driver may handle
FORCE_FD = 0 ; forcing full duplex mode makes sense at some cards and link types
NUM_RX_DESC = 32 ; Number of receive descriptors (must be power of 2)
NUM_TX_DESC = 16 ; Number of transmit descriptors (must be power of 2)
__DEBUG__ = 1 ; 1 = on, 0 = off
__DEBUG_LEVEL__ = 2 ; 1 = verbose, 2 = errors only
; end configureable area
section '.flat' readable writable executable
include '../proc32.inc'
include '../struct.inc'
include '../macros.inc'
include '../fdo.inc'
include '../netdrv.inc'
if (bsr NUM_RX_DESC)>(bsf NUM_RX_DESC)
display 'NUM_RX_DESC must be a power of two'
err
end if
if (bsr NUM_TX_DESC)>(bsf NUM_TX_DESC)
display 'NUM_TX_DESC must be a power of two'
err
end if
; Registers
REG_POWER_MGMT_CTRL = 0x7c
REG_UP_LIST_PTR = 0x38
REG_UP_PKT_STATUS = 0x30
REG_TX_FREE_THRESH = 0x2f
REG_DN_LIST_PTR = 0x24
REG_DMA_CTRL = 0x20
REG_TX_STATUS = 0x1b
REG_RX_STATUS = 0x18
REG_TX_DATA = 0x10
; Common window registers
REG_INT_STATUS = 0xe
REG_COMMAND = 0xe
; Register window 7
REG_MASTER_STATUS = 0xc
REG_POWER_MGMT_EVENT = 0xc
REG_MASTER_LEN = 0x6
REG_VLAN_ETHER_TYPE = 0x4
REG_VLAN_MASK = 0x0
REG_MASTER_ADDRESS = 0x0
; Register window 6
REG_BYTES_XMITTED_OK = 0xc
REG_BYTES_RCVD_OK = 0xa
REG_UPPER_FRAMES_OK = 0x9
REG_FRAMES_DEFERRED = 0x8
REG_FRAMES_RCVD_OK = 0x7
REG_FRAMES_XMITTED_OK = 0x6
REG_RX_OVERRUNS = 0x5
REG_LATE_COLLISIONS = 0x4
REG_SINGLE_COLLISIONS = 0x3
REG_MULTIPLE_COLLISIONS = 0x2
REG_SQE_ERRORS = 0x1
REG_CARRIER_LOST = 0x0
; Register window 5
REG_INDICATION_ENABLE = 0xc
REG_INTERRUPT_ENABLE = 0xa
REG_TX_RECLAIM_THRESH = 0x9
REG_RX_FILTER = 0x8
REG_RX_EARLY_THRESH = 0x6
REG_TX_START_THRESH = 0x0
; Register window 4
REG_UPPER_BYTES_OK = 0xe
REG_BAD_SSD = 0xc
REG_MEDIA_STATUS = 0xa
REG_PHYSICAL_MGMT = 0x8
REG_NETWORK_DIAGNOSTIC = 0x6
REG_FIFO_DIAGNOSTIC = 0x4
REG_VCO_DIAGNOSTIC = 0x2 ; may not supported
; Bits in register window 4
BIT_AUTOSELECT = 24
; Register window 3
REG_TX_FREE = 0xc
REG_RX_FREE = 0xa
REG_MEDIA_OPTIONS = 0x8
REG_MAC_CONTROL = 0x6
REG_MAX_PKT_SIZE = 0x4
REG_INTERNAL_CONFIG = 0x0
; Register window 2
REG_RESET_OPTIONS = 0xc
REG_STATION_MASK_HI = 0xa
REG_STATION_MASK_MID = 0x8
REG_STATION_MASK_LO = 0x6
REG_STATION_ADDRESS_HI = 0x4
REG_STATION_ADDRESS_MID = 0x2
REG_STATION_ADDRESS_LO = 0x0
; Register window 1
REG_TRIGGER_BITS = 0xc
REG_SOS_BITS = 0xa
REG_WAKE_ON_TIMER = 0x8
REG_SMB_RXBYTES = 0x7
REG_SMB_DIAG = 0x5
REG_SMB_ARB = 0x4
REG_SMB_STATUS = 0x2
REG_SMB_ADDRESS = 0x1
REG_SMB_FIFO_DATA = 0x0
; Register window 0
REG_EEPROM_DATA = 0xc
REG_EEPROM_COMMAND = 0xa
REG_BIOS_ROM_DATA = 0x8
REG_BIOS_ROM_ADDR = 0x4
; Physical management bits
BIT_MGMT_DIR = 2 ; drive with the data written in mgmtData
BIT_MGMT_DATA = 1 ; MII management data bit
BIT_MGMT_CLK = 0 ; MII management clock
; MII commands
MII_CMD_MASK = (1111b shl 10)
MII_CMD_READ = (0110b shl 10)
MII_CMD_WRITE = (0101b shl 10)
; eeprom bits and commands
EEPROM_CMD_READ = 0x80
EEPROM_BIT_BUSY = 15
; eeprom registers
EEPROM_REG_OEM_NODE_ADDR= 0xa
EEPROM_REG_CAPABILITIES = 0x10
; Commands for command register
SELECT_REGISTER_WINDOW = (1 shl 11)
; Hw capabilities bitflags
IS_VORTEX = 0x0001
IS_BOOMERANG = 0x0002
IS_CYCLONE = 0x0004
IS_TORNADO = 0x0008
EEPROM_8BIT = 0x0010
HAS_PWR_CTRL = 0x0020
HAS_MII = 0x0040
HAS_NWAY = 0x0080
HAS_CB_FNS = 0x0100
INVERT_MII_PWR = 0x0200
INVERT_LED_PWR = 0x0400
MAX_COLLISION_RESET = 0x0800
EEPROM_OFFSET = 0x1000
HAS_HWCKSM = 0x2000
EXTRA_PREAMBLE = 0x4000
; Status
IntLatch = 0x0001
HostError = 0x0002
TxComplete = 0x0004
TxAvailable = 0x0008
RxComplete = 0x0010
RxEarly = 0x0020
IntReq = 0x0040
StatsFull = 0x0080
DMADone = 0x0100
DownComplete = 0x0200
UpComplete = 0x0400
DMAInProgress = 0x0800 ; 1 shl 11 (DMA controller is still busy)
CmdInProgress = 0x1000 ; 1 shl 12 (EL3_CMD is still busy)
S_5_INTS = HostError + RxEarly + UpComplete + DownComplete + StatsFull ;+ TxComplete + RxComplete + TxAvailable
; Commands
TotalReset = 0 shl 11
SelectWindow = 1 shl 11
StartCoax = 2 shl 11
RxDisable = 3 shl 11
RxEnable = 4 shl 11
RxReset = 5 shl 11
UpStall = 6 shl 11
UpUnstall = (6 shl 11)+1
DownStall = (6 shl 11)+2
DownUnstall = (6 shl 11)+3
RxDiscard = 8 shl 11
TxEnable = 9 shl 11
TxDisable = 10 shl 11
TxReset = 11 shl 11
FakeIntr = 12 shl 11
AckIntr = 13 shl 11
SetIntrEnb = 14 shl 11
SetStatusEnb = 15 shl 11
SetRxFilter = 16 shl 11
SetRxThreshold = 17 shl 11
SetTxThreshold = 18 shl 11
SetTxStart = 19 shl 11
StartDMAUp = 20 shl 11
StartDMADown = (20 shl 11)+1
StatsEnable = 21 shl 11
StatsDisable = 22 shl 11
StopCoax = 23 shl 11
SetFilterBit = 25 shl 11
; Rx mode bits
RxStation = 1
RxMulticast = 2
RxBroadcast = 4
RxProm = 8
MAX_ETH_FRAME_SIZE = 1514
struct tx_desc
next_ptr dd ?
frame_start_hdr dd ?
frag_addr dd ? ; for packet data
frag_len dd ? ; for packet data
realaddr dd ?
rd 3 ; align 32
ends
struct rx_desc
next_ptr dd ?
pkt_status dd ?
frag_addr dd ?
frag_len dd ? ; for packet data
realaddr dd ?
rd 3 ; align 32
ends
struct device ETH_DEVICE
io_addr dd ?
pci_bus dd ?
pci_dev dd ?
irq_line db ?
rb 3 ; alignment
curr_tx dd ?
curr_rx dd ?
prev_tx_frame dd ?
ver_id db ?
full_bus_master db ?
has_hwcksm db ?
preamble db ?
dn_list_ptr_cleared db ?
internal_link dd ? ; link state (to be used only internally by driver)
rb 0x100 - ($ and 0xff) ; align 256
tx_desc_buffer rd (sizeof.tx_desc*NUM_TX_DESC)/4
rx_desc_buffer rd (sizeof.rx_desc*NUM_RX_DESC)/4
ends
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; proc START ;;
;; ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc START c, reason:dword, cmdline:dword
cmp [reason], DRV_ENTRY
jne .fail
DEBUGF 2,"Loading driver\n"
invoke RegService, my_service, service_proc
ret
.fail:
xor eax, eax
ret
endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; proc SERVICE_PROC ;;
;; ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
align 4
proc service_proc stdcall, ioctl:dword
mov edx, [ioctl]
mov eax, [edx + IOCTL.io_code]
;------------------------------------------------------
cmp eax, 0 ;SRV_GETVERSION
jne @F
cmp [edx + IOCTL.out_size], 4
jb .fail
mov eax, [edx + IOCTL.output]
mov [eax], dword API_VERSION
xor eax, eax
ret
;------------------------------------------------------
@@:
cmp eax, 1 ;SRV_HOOK
jne .fail
cmp [edx + IOCTL.inp_size], 3 ; Data input must be at least 3 bytes
jb .fail
mov eax, [edx + IOCTL.input]
cmp byte [eax], 1 ; 1 means device number and bus number (pci) are given
jne .fail ; other types of this hardware dont exist
; check if the device is already listed
mov ecx, [devices]
test ecx, ecx
jz .firstdevice
mov esi, device_list
mov eax, [edx + IOCTL.input] ; get the pci bus and device numbers
mov ax , [eax+1] ;
.nextdevice:
mov ebx, [esi]
cmp al, byte[ebx + device.pci_bus]
jne @f
cmp ah, byte[ebx + device.pci_dev]
je .find_devicenum ; Device is already loaded, let's find it's device number
@@:
add esi, 4
loop .nextdevice
; This device doesnt have its own eth_device structure yet, lets create one
.firstdevice:
cmp [devices], MAX_DEVICES ; First check if the driver can handle one more card
jae .fail
allocate_and_clear ebx, sizeof.device, .fail ; Allocate the buffer for device structure
; Fill in the direct call addresses into the struct
mov [ebx + device.reset], reset
mov [ebx + device.transmit], null_op
mov [ebx + device.unload], null_op
mov [ebx + device.name], my_service
; save the pci bus and device numbers
mov eax, [edx + IOCTL.input]
movzx ecx, byte[eax+1]
mov [ebx + device.pci_bus], ecx
movzx ecx, byte[eax+2]
mov [ebx + device.pci_dev], ecx
; Now, it's time to find the base io addres of the PCI device
stdcall PCI_find_io, [ebx + device.pci_bus], [ebx + device.pci_dev]
mov [ebx + device.io_addr], eax
; We've found the io address, find IRQ now
invoke PciRead8, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.interrupt_line
mov [ebx + device.irq_line], al
DEBUGF 1,"Hooking into device, dev:%x, bus:%x, irq:%x, addr:%x\n",\
[ebx + device.pci_dev]:1,[ebx + device.pci_bus]:1,[ebx + device.irq_line]:1,[ebx + device.io_addr]:4
; Ok, the eth_device structure is ready, let's probe the device
call probe ; this function will output in eax
test eax, eax
jnz .err ; If an error occured, exit
mov eax, [devices] ; Add the device structure to our device list
mov [device_list+4*eax], ebx ;
inc [devices] ;
.register:
mov [ebx + device.type], NET_TYPE_ETH
invoke NetRegDev
cmp eax, -1
je .destroy
call start_device
ret
; If the device was already loaded, find the device number and return it in eax
.find_devicenum:
DEBUGF 2,"Trying to find device number of already registered device\n"
invoke NetPtrToNum ; This kernel procedure converts a pointer to device struct in ebx
; into a device number in edi
mov eax, edi ; Application wants it in eax instead
DEBUGF 2,"Kernel says: %u\n", eax
ret
; If an error occured, remove all allocated data and exit (returning -1 in eax)
.destroy:
; todo: reset device into virgin state
.err:
invoke KernelFree, ebx
.fail:
DEBUGF 2, "Failed to load\n"
or eax, -1
ret
;------------------------------------------------------
endp
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
;; ;;
;; Actual Hardware dependent code starts here ;;
;; ;;
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
align 4
null_op:
ret
;***************************************************************************
; Function
; probe
; Description
; Searches for an ethernet card, enables it and clears the rx buffer
; Destroyed registers
; eax, ebx, ecx, edx, edi, esi
;
;***************************************************************************
align 4
probe:
DEBUGF 1,"Probing 3com card\n"
; Make the device a bus master
invoke PciRead32, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.command
or al, PCI_CMD_MASTER
invoke PciWrite32, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.command, eax
; wake up the card
DEBUGF 1,"Checking if the device is awake\n"
; wake up - we directly do it by programming PCI
; check if the device is power management capable
invoke PciRead16, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.status
test al, PCI_STATUS_CAPA ; is there "new capabilities" linked list?
jz .device_awake
; search for power management register
invoke PciRead8, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.cap_ptr
and al, not 3
cmp al, 0x3f
jbe .device_awake ; invalid pointer if less then PCI header size
; traverse the list
movzx esi, al
.pm_loop:
invoke PciRead16, [ebx + device.pci_bus], [ebx + device.pci_dev], esi
cmp al, 1
je .set_pm_state
movzx esi, ah ; load address of next capability
test ah, ah ; 0 if none left
jnz .pm_loop
jmp .device_awake
; wake up the device if necessary
.set_pm_state:
DEBUGF 1,"Waking up the device\n"
add esi, 4 ; offset for power management
invoke PciRead32, [ebx + device.pci_bus], [ebx + device.pci_dev], esi
test al, 11b
jz .device_awake
and al, not 11b ; set state to D0
invoke PciWrite32, [ebx + device.pci_bus], [ebx + device.pci_dev], esi, eax
.device_awake:
DEBUGF 1,"Device is awake\n"
; Check device/vendor ID
invoke PciRead32, [ebx + device.pci_bus], [ebx + device.pci_dev], 0
DEBUGF 1,"Vendor id: 0x%x\n", ax
cmp ax, 0x10B7
jne .notfound
shr eax, 16
DEBUGF 1,"Vendor ok!, device id: 0x%x\n", ax
; get chip version
mov ecx, HW_VERSIONS_SIZE/4-1
.loop:
cmp ax, [hw_versions+ecx*4]
jz .found
loop .loop
.notfound:
DEBUGF 2,"Device id not found in list!\n"
or eax, -1
ret
.found:
mov esi, [hw_str+ecx*4]
DEBUGF 1,"Hardware type: %s\n", esi
mov [ebx + device.name], esi
mov [ebx + device.ver_id], cl
test word [hw_versions+2+ecx*4], HAS_HWCKSM
setnz [ebx + device.has_hwcksm]
; set pci latency for vortex cards
test word [hw_versions+2+ecx*4], IS_VORTEX
jz .not_vortex
mov al, 11111000b ; 248 = max latency
invoke PciWrite8, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.max_latency, eax
.not_vortex:
; set RX/TX functions
mov ax, EEPROM_REG_CAPABILITIES
call read_eeprom
test al, 100000b ; full bus master?
setnz [ebx + device.full_bus_master]
jnz .boomerang_func
mov [ebx + device.transmit], vortex_transmit
DEBUGF 2,"Device is a vortex type\n"
DEBUGF 2,"I'm sorry but vortex code hasnt been tested yet\n"
DEBUGF 2,"Please contact me on hidnplayr@kolibrios.org\n"
DEBUGF 2,"If you can help me finish it!\n"
or eax, -1
ret
jmp @f
.boomerang_func: ; full bus master, so use boomerang functions
mov [ebx + device.transmit], boomerang_transmit
DEBUGF 1,"Device is a boomerang type\n"
@@:
call read_mac_eeprom
test byte [ebx + device.full_bus_master], 0xff
jz .set_preamble
; switch to register window 2
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+2
out dx, ax
; activate xcvr by setting some magic bits
set_io [ebx + device.io_addr], REG_RESET_OPTIONS
in ax, dx
and ax, not 0x4010
movzx ecx, [ebx + device.ver_id]
test word [ecx*4+hw_versions+2], INVERT_LED_PWR
jz @f
or al, 0x10
@@:
test word [ecx*4+hw_versions+2], INVERT_MII_PWR
jz @f
or ah, 0x40
@@:
out dx, ax
.set_preamble:
; use preamble as default
mov byte [ebx + device.preamble], 1 ; enable preamble
DEBUGF 1,"Global reset..\n"
; GlobalReset
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
xor eax, eax
; or al, 0x14
out dx, ax
; wait for GlobalReset to complete
mov ecx, 2000
.rsloop:
in ax, dx
test ah, 10000b ; CmdInProgress
pusha
mov esi, 1
invoke Sleep
popa
loopz .rsloop
DEBUGF 1,"Waiting for NIC to boot..\n"
; wait for 2 seconds for NIC to boot
mov esi, 2000 ; WTF? FIXME
invoke Sleep ; 2 seconds
DEBUGF 1,"Ok!\n"
;--------------------------
;
; RESET
;
;--------------------------
reset:
movzx eax, [ebx + device.irq_line]
DEBUGF 1,"Attaching int handler to irq %x\n", eax:1
movzx ecx, [ebx + device.ver_id]
test word [hw_versions+2+ecx*4], IS_VORTEX
jz .not_vortex
mov esi, int_vortex
jmp .reg_int
.not_vortex:
mov esi, int_boomerang
.reg_int:
invoke AttachIntHandler, eax, esi, ebx
test eax, eax
jnz @f
DEBUGF 2,"Could not attach int handler!\n"
or eax, -1
ret
@@:
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW + 0
out dx, ax
mov ax, StopCoax
out dx, ax ; stop transceiver
mov ax, SELECT_REGISTER_WINDOW + 4
out dx, ax ; disable UTP
set_io [ebx + device.io_addr], REG_MEDIA_STATUS
mov ax, 0x0
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW + 0
out dx, ax
set_io [ebx + device.io_addr], REG_FIFO_DIAGNOSTIC
mov ax, 0
out dx, ax ; disable card
mov ax, 1
out dx, ax ; enable card
call write_mac
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW + 1
out dx, ax
mov ecx, 32
set_io [ebx + device.io_addr], 0x0b
.loop:
in al, dx
loop .loop
; Get rid of stray ints
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, AckIntr + 0xff
out dx, ax
mov ax, SetStatusEnb + S_5_INTS
out dx, ax
mov ax, SetIntrEnb + S_5_INTS
out dx, ax
DEBUGF 1,"Setting RX mode\n"
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
if defined PROMISCIOUS
mov ax, SetRxFilter + RxStation + RxMulticast + RxBroadcast + RxProm
else if defined ALLMULTI
mov ax, SetRxFilter + RxStation + RxMulticast + RxBroadcast
else
mov ax, SetRxFilter + RxStation + RxBroadcast
end if
out dx, ax
call set_active_port
call create_rx_ring
test eax, eax
jnz .err
call rx_reset
call tx_reset
xor eax, eax
; clear packet/byte counters
lea edi, [ebx + device.bytes_tx]
mov ecx, 6
rep stosd
xor eax, eax
ret
.err:
DEBUGF 2,"reset failed\n"
or eax, -1
ret
align 4
start_device:
DEBUGF 1,"Starting the device\n"
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, SetTxThreshold + 60 ;2047 ; recommended by the manual :)
out dx, ax
call check_tx_status
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
; switch to register window 4
mov ax, SELECT_REGISTER_WINDOW+4
out dx, ax
; wait for linkDetect
set_io [ebx + device.io_addr], REG_MEDIA_STATUS
mov ecx, 20 ; wait for max 2s
.link_detect_loop:
mov esi, 100
invoke Sleep ; 100 ms
in ax, dx
test ah, 1000b ; linkDetect
jnz @f
loop .link_detect_loop
DEBUGF 2,"Link detect timed-out!\n"
@@:
; print link type
xor eax, eax
bsr ax, word[ebx + device.internal_link]
jz @f
sub ax, 5
@@:
mov esi, [link_str+eax*4]
DEBUGF 1,"Established Link type: %s\n", esi
; enable interrupts
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW + 1
out dx, ax
mov ax, AckIntr + 0xff
out dx, ax
mov ax, SetStatusEnb + S_5_INTS
out dx, ax
mov ax, SetIntrEnb + S_5_INTS
out dx, ax
; Start RX/TX
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, RxEnable
out dx, ax
mov ax, TxEnable
out dx, ax
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, SetRxThreshold + 208
out dx, ax
mov ax, SetTxThreshold + 60 ;16 ; recommended by the manual :)
out dx, ax
mov ax, SELECT_REGISTER_WINDOW + 1
out dx, ax
; Set the mtu, kernel will be able to send now
mov [ebx + device.mtu], 1514
; Set link state to unknown
mov [ebx + device.state], ETH_LINK_UNKNOWN
xor eax, eax
ret
;***************************************************************************
; Function
; tx_reset
; Description
; resets and enables transmitter engine
;
;***************************************************************************
align 4
tx_reset:
DEBUGF 1,"tx reset\n"
; TxReset
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, TxReset
out dx, ax
; Wait for TxReset to complete
mov ecx, 200000
.tx_reset_loop:
in ax, dx
test ah, 10000b ; check CmdInProgress
jz .tx_set_prev
dec ecx
jnz .tx_reset_loop
.tx_set_prev:
; init last TX descriptor
lea eax, [ebx + device.tx_desc_buffer + (NUM_TX_DESC-1)*sizeof.tx_desc]
mov [ebx + device.curr_tx], eax
.tx_enable:
ret
;***************************************************************************
; Function
; rx_reset
; Description
; resets and enables receiver engine
;
;***************************************************************************
align 4
rx_reset:
DEBUGF 1,"rx reset\n"
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, RxReset or 0x4
out dx, ax
; wait for RxReset to complete
mov ecx, 200000
.loop:
in ax, dx
test ah, 10000b ; check CmdInProgress
jz .done
dec ecx
jnz .loop
.done:
lea eax, [ebx + device.rx_desc_buffer]
mov [ebx + device.curr_rx], eax
invoke GetPhysAddr
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_UP_LIST_PTR
out dx, eax
.rx_enable:
ret
align 4
create_rx_ring:
; create rx descriptor ring
lea eax, [ebx + device.rx_desc_buffer]
invoke GetPhysAddr
mov edi, eax ; real addr of first descr
lea esi, [ebx + device.rx_desc_buffer] ; ptr to first descr
lea edx, [ebx + device.rx_desc_buffer + (NUM_RX_DESC-1)*sizeof.rx_desc] ; ptr to last descr
mov ecx, NUM_RX_DESC
.loop:
mov [edx + rx_desc.next_ptr], edi
push ecx edx
invoke NetAlloc, MAX_ETH_FRAME_SIZE+NET_BUFF.data
pop edx ecx
test eax, eax
jz .out_of_mem
mov [esi + rx_desc.realaddr], eax
invoke GetPhysAddr
add eax, NET_BUFF.data
mov [esi + rx_desc.frag_addr], eax
and [esi + rx_desc.pkt_status], 0
mov [esi + rx_desc.frag_len], MAX_ETH_FRAME_SIZE or (1 shl 31)
DEBUGF 1,"rx_desc: lin=%x phys=%x len=%x next ptr=%x\n", [esi+rx_desc.realaddr]:8, [esi+rx_desc.frag_addr]:8, [esi+rx_desc.frag_len]:8, edi
DEBUGF 1,"rx_desc: cur=%x prev=%x\n", esi, edx
mov edx, esi
add esi, sizeof.rx_desc
add edi, sizeof.rx_desc
dec ecx
jnz .loop
xor eax, eax
ret
.out_of_mem:
or eax, -1
ret
;---------------------------------------------------------------------------
; Function
; try_link_detect
; Description
; try_link_detect checks if link exists
; Parameters
; ebx = device structure
; Return value
; al - 0 ; no link detected
; al - 1 ; link detected
; Destroyed registers
; eax, ebx, ecx, edx, edi, esi
;
;---------------------------------------------------------------------------
align 4
try_link_detect:
DEBUGF 1,"Trying to detect link\n"
; create self-directed packet
invoke NetAlloc, 20+NET_BUFF.data ; create a buffer for the self-directed packet
test eax, eax
jz .fail
push eax
mov [eax + NET_BUFF.length], 20
lea edi, [eax + NET_BUFF.data]
lea esi, [ebx + device.mac]
movsw
movsd
sub esi, 6
movsw
movsd
mov ax , 0x0608
stosw
; download self-directed packet
call [ebx + device.transmit]
; switch to register window 4
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+4
out dx, ax
; See if we have received the packet by now..
cmp [ebx + device.packets_rx], 0
jnz .link_detected
; switch to register window 4
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+4
out dx, ax
; read linkbeatdetect
set_io [ebx + device.io_addr], REG_MEDIA_STATUS
in ax, dx
test ah, 1000b ; test linkBeatDetect
jnz .link_detected
xor al, al
jmp .finish
.link_detected:
DEBUGF 1,"Link detected!\n"
setb al
.finish:
test al, al
jz @f
or byte [ebx + device.internal_link+1], 100b
@@:
xor eax, eax
ret
.fail:
DEBUGF 1,"No link detected!\n"
or eax, -1
ret
;***************************************************************************
; Function
; try_phy
; Description
; try_phy checks the auto-negotiation function
; in the PHY at PHY index. It can also be extended to
; include link detection for non-IEEE 802.3u
; auto-negotiation devices, for instance the BCM5000. ; TODO: BCM5000
; Parameters
; ah - PHY index
; ebx - device stucture
; Return value
; al - 0 link is auto-negotiated
; al - 1 no link is auto-negotiated
; Destroyed registers
; eax, ebx, ecx, edx, esi
;
;***************************************************************************
align 4
try_phy:
DEBUGF 1,"PHY=%u\n", ah
DEBUGF 1,"Detecting if device is auto-negotiation capable\n"
mov al, MII_BMCR
push eax
call mdio_read
or ax, BMCR_RESET
mov esi, eax
mov eax, [esp]
call mdio_write
; wait for reset to complete
mov esi, 2000
invoke Sleep ; 2s FIXME
mov eax, [esp]
mov al, MII_BMCR
call mdio_read
test ax, BMCR_RESET
jnz .fail1
; wait for a while after reset
mov esi, 20
invoke Sleep ; 20ms
mov eax, [esp]
mov al, MII_BMSR
call mdio_read
test al, BMSR_ERCAP
jz .fail2
DEBUGF 1,"Extended capability supported\n"
test al, BMSR_ANEGCAPABLE
jz .fail2
DEBUGF 1,"Auto-negotiation capable\n"
test al, BMSR_ANEGCOMPLETE
jnz .auto_neg_ok
DEBUGF 1,"Restarting auto-negotiation\n"
; restart auto-negotiation
mov eax, [esp]
mov al, MII_ADVERTISE
push eax
call mdio_read ; returns with window #4
or ax, ADVERTISE_10HALF or ADVERTISE_10FULL or ADVERTISE_100HALF or ADVERTISE_100FULL
mov esi, eax
pop eax
call mdio_write ; returns with window #4
mov eax, [esp]
call mdio_read ; returns with window #4
mov esi, eax
or bx, BMCR_ANENABLE or BMCR_ANRESTART
mov eax, [esp]
call mdio_write ; returns with window #4
mov esi, 4000
invoke Sleep ; 4 seconds
mov eax, [esp]
mov al, MII_BMSR
call mdio_read ; returns with window #4
test al, BMSR_ANEGCOMPLETE
jnz .auto_neg_ok
jmp .fail3
.auto_neg_ok:
DEBUGF 1,"Auto-negotiation complete\n"
; compare advertisement and link partner ability registers
mov eax, [esp]
mov al, MII_ADVERTISE
call mdio_read ; returns with window #4
xchg eax, [esp]
mov al, MII_LPA
call mdio_read ; returns with window #4
pop esi
and eax, esi
and eax, 11111100000b ; Mask off
push eax
mov word[ebx + device.internal_link+2], ax
; switch to register window 3
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+3
out dx, ax
; set full-duplex mode
set_io [ebx + device.io_addr], REG_MAC_CONTROL
in ax, dx
and ax, not 0x120 ; clear full duplex and flow control
pop esi
test esi, 1010b shl 5 ; check for full-duplex
jz .half_duplex
or ax, 0x120 ; set full duplex and flow control
.half_duplex:
DEBUGF 1,"Using half-duplex\n"
out dx, ax
mov al, 1
ret
.fail1:
DEBUGF 2,"reset failed!\n"
pop eax
xor al, al
ret
.fail2:
DEBUGF 2,"This device is not auto-negotiation capable!\n"
pop eax
xor al, al
ret
.fail3:
DEBUGF 2,"Auto-negotiation reset failed!\n"
pop eax
xor al, al
ret
;***************************************************************************
; Function
; try_mii
; Description
; try_MII checks the on-chip auto-negotiation logic
; or an off-chip MII PHY, depending upon what is set in
; xcvrSelect by the caller.
; It exits when it finds the first device with a good link.
; Parameters
;
; Return value
; al - 0
; al - 1
; Destroyed registers
; eax, ebx, ecx, edx, esi
;
;***************************************************************************
align 4
try_mii:
DEBUGF 1,"Trying to find MII PHY\n"
; switch to register window 3
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+3
out dx, ax
set_io [ebx + device.io_addr], REG_INTERNAL_CONFIG
in eax, dx
and eax, (1111b shl 20)
cmp eax, (1000b shl 20) ; is auto-negotiation set?
jne .mii_device
DEBUGF 1,"Auto-negotiation is set\n"
; switch to register window 4
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+4
out dx, ax
; PHY==24 is the on-chip auto-negotiation logic
; it supports only 10base-T and 100base-TX
mov ah, 24
call try_phy
test al, al
jz .fail
mov cl, 24
jmp .check_preamble
.mii_device:
cmp eax, (0110b shl 20)
jne .fail
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
mov ax , SELECT_REGISTER_WINDOW+4
out dx , ax
set_io [ebx + device.io_addr], REG_PHYSICAL_MGMT
in ax , dx
and al , (1 shl BIT_MGMT_DIR) or (1 shl BIT_MGMT_DATA)
cmp al , (1 shl BIT_MGMT_DATA)
je .search_for_phy
xor al, al
ret
.search_for_phy:
; search for PHY
mov cx, 31
.search_phy_loop:
DEBUGF 1,"Searching for the PHY\n"
cmp cx, 24
je .next_phy
mov ah, cl ; ah = phy
mov al, MII_BMSR ; al = Basic Mode Status Register ; BUGFIX HERE! :):)
push cx
call mdio_read
pop cx
test ax, ax
jz .next_phy
cmp ax, 0xffff
je .next_phy
mov ah, cl ; ah = phy
push cx
call try_phy
pop cx
test al, al
jnz .check_preamble
.next_phy:
loopw .search_phy_loop
.fail:
DEBUGF 1,"PHY not found\n"
xor al, al
ret
; epilog
.check_preamble:
DEBUGF 1,"Using PHY: %u\nChecking PreAmble\n", cl
push eax ; eax contains the return value of try_phy
; check hard coded preamble forcing
movzx eax, [ebx + device.ver_id]
test word [eax*4+hw_versions+2], EXTRA_PREAMBLE
setnz [ebx + device.preamble] ; force preamble
jnz .finish
; check mii for preamble suppression
mov ah, cl
mov al, MII_BMSR
call mdio_read
test al, 1000000b ; preamble suppression?
setz [ebx + device.preamble] ; no
.finish:
pop eax
ret
;***************************************************************************
; Function
; test_packet
; Description
; try_loopback try a loopback packet for 10BASE2 or AUI port
; Parameters
; ebx = device structure
;
;***************************************************************************
align 4
test_packet:
DEBUGF 1,"Sending test packet\n"
; switch to register window 3
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+3
out dx, ax
; set fullDuplexEnable in MacControl register
set_io [ebx + device.io_addr], REG_MAC_CONTROL
in ax, dx
or ax, 0x120
out dx, ax
; switch to register window 5
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+5
out dx, ax
; set RxFilter to enable individual address matches
mov ax, (10000b shl 11)
set_io [ebx + device.io_addr], REG_RX_FILTER
in al, dx
or al, 1
set_io [ebx + device.io_addr], REG_COMMAND
out dx, ax
; issue RxEnable and TxEnable
call rx_reset
call tx_reset
; create self-directed packet
invoke NetAlloc, 20 + NET_BUFF.data ; create a buffer for the self-directed packet
test eax, eax
jz .fail
push eax
mov [eax + NET_BUFF.length], 20
; mov [eax + NET_BUFF.device], ebx
lea edi, [eax + NET_BUFF.data]
lea esi, [ebx + device.mac]
movsw
movsd
sub esi, 6
movsw
movsd
mov ax, 0x0608
stosw
; download self-directed packet
call [ebx + device.transmit]
; wait for 2s
mov esi, 2000 ; FIXME
invoke Sleep
; check if self-directed packet is received
mov eax, [ebx + device.packets_rx]
test eax, eax
jnz .finish
; switch to register window 3
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+3
out dx, ax
; clear fullDuplexEnable in MacControl register
set_io [ebx + device.io_addr], REG_MAC_CONTROL
in ax, dx
and ax, not 0x120
out dx, ax
.fail:
xor eax, eax
.finish:
ret
;***************************************************************************
; Function
; try_loopback
; Description
; tries a loopback packet for 10BASE2 or AUI port
; Parameters
; al - 0: 10Mbps AUI connector
; 1: 10BASE-2
;
; Return value
; al - 0
; al - 1
; Destroyed registers
; eax, ebx, ecx, edx, edi, esi
;
;***************************************************************************
align 4
try_loopback:
DEBUGF 1,"trying loopback\n"
push eax
; switch to register window 3
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+3
out dx, ax
mov eax, [esp]
mov cl, al
inc cl
shl cl, 3
or byte [ebx + device.internal_link+1], cl
test al, al ; aui or coax?
jz .complete_loopback
; enable 100BASE-2 DC-DC converter
mov ax, (10b shl 11) ; EnableDcConverter
out dx, ax
.complete_loopback:
mov cx, 2 ; give a port 3 chances to complete a loopback
.next_try:
push ecx
call test_packet
pop ecx
test eax, eax
loopzw .next_try
.finish:
xchg eax, [esp]
test al, al
jz .aui_finish
; issue DisableDcConverter command
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, (10111b shl 11)
out dx, ax
.aui_finish:
pop eax ; al contains the result of operation
test al, al
jnz @f
and byte [ebx + device.internal_link+1], not 11000b
@@:
ret
;***************************************************************************
; Function
; set_active_port
; Description
; It selects the media port (transceiver) to be used
; Return value:
; Destroyed registers
; eax, ebx, ecx, edx, edi, esi
;
;***************************************************************************
align 4
set_active_port:
DEBUGF 1,"Trying to find the active port\n"
; switch to register window 3
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW + 3
out dx, ax
set_io [ebx + device.io_addr], REG_INTERNAL_CONFIG
in eax, dx
test eax, (1 shl 24) ; check if autoselect enable
jz .set_first_available_media
; check 100BASE-TX and 10BASE-T
set_io [ebx + device.io_addr], REG_MEDIA_OPTIONS
in ax, dx
test al, 1010b ; check whether 100BASE-TX or 10BASE-T available
jz .mii_device ; they are not available
; set auto-negotiation
set_io [ebx + device.io_addr], REG_INTERNAL_CONFIG
in eax, dx
and eax, not (1111b shl 20)
or eax, (1000b shl 20)
out dx, eax
call try_mii
test al, al
jz .mii_device
DEBUGF 1,"Using auto negotiation\n"
ret
.mii_device:
; switch to register window 3
set_io [ebx + device.io_addr], 0
; check for off-chip mii device
set_io [ebx + device.io_addr], REG_MEDIA_OPTIONS
in ax, dx
test al, 1000000b ; check miiDevice
jz .base_fx
set_io [ebx + device.io_addr], REG_INTERNAL_CONFIG
in eax, dx
and eax, not (1111b shl 20)
or eax, (0110b shl 20) ; set MIIDevice
out dx, eax
call try_mii
test al, al
jz .base_fx
DEBUGF 1,"Using off-chip mii device\n"
ret
.base_fx:
; switch to register window 3
set_io [ebx + device.io_addr], 0
; check for 100BASE-FX
set_io [ebx + device.io_addr], REG_MEDIA_OPTIONS
in ax, dx ; read media option register
test al, 100b ; check 100BASE-FX
jz .aui_enable
set_io [ebx + device.io_addr], REG_INTERNAL_CONFIG
in eax, dx
and eax, not (1111b shl 20)
or eax, (0101b shl 20) ; set 100base-FX
out dx, eax
call try_link_detect
test al, al
jz .aui_enable
DEBUGF 1,"Using 100Base-FX\n"
ret
.aui_enable:
; switch to register window 3
set_io [ebx + device.io_addr], 0
; check for 10Mbps AUI connector
set_io [ebx + device.io_addr], REG_MEDIA_OPTIONS
in ax, dx ; read media option register
test al, 100000b ; check 10Mbps AUI connector
jz .coax_available
set_io [ebx + device.io_addr], REG_INTERNAL_CONFIG
in eax, dx
and eax, not (1111b shl 20)
or eax, (0001b shl 20) ; set 10Mbps AUI connector
out dx, eax
xor al, al ; try 10Mbps AUI connector
call try_loopback
test al, al
jz .coax_available
DEBUGF 1,"Using 10Mbps aui\n"
ret
.coax_available:
; switch to register window 3
set_io [ebx + device.io_addr], 0
; check for coaxial 10BASE-2 port
set_io [ebx + device.io_addr], REG_MEDIA_OPTIONS
in ax, dx ; read media option register
test al, 10000b ; check 10BASE-2
jz .set_first_available_media
set_io [ebx + device.io_addr], REG_INTERNAL_CONFIG
in eax, dx
and eax, not (1111b shl 20)
or eax, (0011b shl 20) ; set 10BASE-2
out dx, eax
mov al, 1
call try_loopback
test al, al
jz .set_first_available_media
DEBUGF 1,"Using 10BASE-2 port\n"
ret
.set_first_available_media:
DEBUGF 1,"Using the first available media\n"
;***************************************************************************
; Function
; set_available_media
; Description
; sets the first available media
; Parameters
; ebx - ptr to device struct
; Return value
; al - 0
; al - 1
; Destroyed registers
; eax, edx
;
;***************************************************************************
align 4
set_available_media:
DEBUGF 1,"Setting the available media\n"
; switch to register window 3
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+3
out dx, ax
set_io [ebx + device.io_addr], REG_MEDIA_OPTIONS
in ax, dx
DEBUGF 1,"available media:%x\n", al
mov cl, al
set_io [ebx + device.io_addr], REG_INTERNAL_CONFIG
in eax, dx
and eax, not (1111b shl 20) ; these bits hold the 'transceiver select' value
test cl, 10b ; baseTXAvailable
jz @f
DEBUGF 1,"base TX is available\n"
or eax, (100b shl 20)
if FORCE_FD
mov word [ebx + device.internal_link], (1 shl 8)
else
mov word [ebx + device.internal_link], (1 shl 7)
end if
jmp .set_media
@@:
test cl, 100b ; baseFXAvailable
jz @f
DEBUGF 1,"base FX is available\n"
or eax, (101b shl 20)
mov word [ebx + device.internal_link], (1 shl 10)
jmp .set_media
@@:
test cl, 1000000b ; miiDevice
jz @f
DEBUGF 1,"mii-device is available\n"
or eax, (0110b shl 20)
mov word [ebx + device.internal_link], (1 shl 13)
jmp .set_media
@@:
test cl, 1000b ; 10bTAvailable
jz @f
DEBUGF 1,"10base-T is available\n"
.set_default:
if FORCE_FD
mov word [ebx + device.internal_link], (1 shl 6)
else
mov word [ebx + device.internal_link], (1 shl 5)
end if
jmp .set_media
@@:
test cl, 10000b ; coaxAvailable
jz @f
DEBUGF 1,"coax is available\n"
push eax
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, (10b shl 11) ; EnableDcConverter
out dx, ax
pop eax
or eax, (11b shl 20)
mov word [ebx + device.internal_link], (1 shl 12)
jmp .set_media
@@:
test cl, 10000b ; auiAvailable
jz .set_default
DEBUGF 1,"AUI is available\n"
or eax, (1 shl 20)
mov word [ebx + device.internal_link], (1 shl 11)
.set_media:
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_INTERNAL_CONFIG
out dx, eax
if FORCE_FD
DEBUGF 1,"Forcing full duplex\n"
set_io [ebx + device.io_addr], REG_MAC_CONTROL
in ax, dx
or ax, 0x120
out dx, ax
end if
mov al, 1
ret
;***************************************************************************
; Function
; write_eeprom
; Description
; reads eeprom
; Note : the caller must switch to the register window 0
; before calling this function
; Parameters:
; ax - register to be read (only the first 63 words can be read)
; cx - value to be read into the register
; Return value:
; ax - word read
; Destroyed registers
; ax, ebx, edx
;
;***************************************************************************
; align 4
;write_eeprom:
; mov edx, [io_addr]
; add edx, REG_EEPROM_COMMAND
; cmp ah, 11b
; ja .finish ; address may have a value of maximal 1023
; shl ax, 2
; shr al, 2
; push eax
;; wait for busy
; mov ebx, 0xffff
;@@:
; in ax, dx
; test ah, 0x80
; jz .write_enable
; dec ebx
; jns @r
;; write enable
;.write_enable:
; xor eax, eax
; mov eax, (11b shl 4)
; out dx, ax
;; wait for busy
; mov ebx, 0xffff
;@@:
; in ax, dx
; test ah, 0x80
; jz .erase_loop
; dec ebx
; jns @r
;.erase_loop:
; pop eax
; push eax
; or ax, (11b shl 6) ; erase register
; out dx, ax
; mov ebx, 0xffff
;@@:
; in ax, dx
; test ah, 0x80
; jz .write_reg
; dec ebx
; jns @r
;.write_reg:
; add edx, REG_EEPROM_DATA-REG_EEPROM_COMMAND
; mov eax, ecx
; out dx, ax
;; write enable
; add edx, REG_EEPROM_COMMAND-REG_EEPROM_DATA
; xor eax, eax
; mov eax, (11b shl 4)
; out dx, ax
; wait for busy
; mov ebx, 0xffff
;@@:
; in ax, dx
; test ah, 0x80
; jz .issue_write_reg
; dec ebx
; jns @r
;.issue_write_reg:
; pop eax
; or ax, 01b shl 6
; out dx, ax
;.finish:
; ret
;***************************************************************************
; Function
; read_eeprom
; Description
; reads eeprom
; Parameters:
; ax - register to be read (only the first 63 words can be read)
; ebx = driver structure
; Return value:
; ax - word read
; Destroyed registers
; ax, ebx, edx
;
;***************************************************************************
align 4
read_eeprom:
DEBUGF 1,"Reading from eeprom.. "
push eax
; switch to register window 0
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+0
out dx, ax
pop eax
and ax, 111111b ; take only the first 6 bits into account
movzx esi, [ebx + device.ver_id]
test word [esi*4+hw_versions+2], EEPROM_8BIT
jz @f
add ax, 0x230 ; hardware constant
jmp .read
@@:
add ax, EEPROM_CMD_READ
test word [esi*4+hw_versions+2], EEPROM_OFFSET
jz .read
add ax, 0x30
.read:
set_io [ebx + device.io_addr], REG_EEPROM_COMMAND
out dx, ax
mov ecx, 0xffff ; duration of about 162 us ;-)
.wait_for_reading:
in ax, dx
test ah, 0x80 ; check bit eepromBusy
jz .read_data
loop .wait_for_reading
.read_data:
set_io [ebx + device.io_addr], REG_EEPROM_DATA
in ax, dx
DEBUGF 1,"ok!\n"
ret
;***************************************************************************
; Function
; mdio_sync
; Description
; initial synchronization
; Parameters
;
; Return value
; Destroyed registers
; ax, edx, cl
;
;***************************************************************************
align 4
mdio_sync:
DEBUGF 1,"syncing mdio\n"
; switch to register window 4
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+4
out dx, ax
cmp [ebx + device.preamble], 0
je .no_preamble
; send 32 logic ones
set_io [ebx + device.io_addr], REG_PHYSICAL_MGMT
mov ecx, 31
.loop:
mov ax, (1 shl BIT_MGMT_DATA) or (1 shl BIT_MGMT_DIR)
out dx, ax
in ax, dx ; delay
mov ax, (1 shl BIT_MGMT_DATA) or (1 shl BIT_MGMT_DIR) or (1 shl BIT_MGMT_CLK)
out dx, ax
in ax, dx ; delay
loop .loop
.no_preamble:
ret
;***************************************************************************
; Function
; mdio_read
; Description
; read MII register
; see page 16 in D83840A.pdf
; Parameters
; ah - PHY addr
; al - register addr
; ebx = device structure
; Return value
; ax - register read
;
;***************************************************************************
align 4
mdio_read:
DEBUGF 1,"Reading MII registers\n"
push eax
call mdio_sync ; returns with window #4
pop eax
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_PHYSICAL_MGMT
shl al, 3
shr ax, 3
and ax, not MII_CMD_MASK
or ax, MII_CMD_READ
mov esi, eax
mov ecx, 13
.cmd_loop:
mov ax, (1 shl BIT_MGMT_DIR) ; write mii
bt esi, ecx
jnc .zero_bit
or al, (1 shl BIT_MGMT_DATA)
.zero_bit:
out dx, ax
push ax
in ax, dx ; delay
pop ax
or al, (1 shl BIT_MGMT_CLK) ; write
out dx, ax
in ax, dx ; delay
loop .cmd_loop
; read data (18 bits with the two transition bits)
mov ecx, 17
xor esi, esi
.read_loop:
shl esi, 1
xor eax, eax ; read comand
out dx, ax
in ax, dx ; delay
in ax, dx
test al, (1 shl BIT_MGMT_DATA)
jz .dont_set
inc esi
.dont_set:
mov ax, (1 shl BIT_MGMT_CLK)
out dx, ax
in ax, dx ; delay
loop .read_loop
mov eax, esi
ret
;***************************************************************************
; Function
; mdio_write
; Description
; write MII register
; see page 16 in D83840A.pdf
; Parameters
; ah - PHY addr
; al - register addr
; si - word to be written
; Return value
; ax - register read
;
;***************************************************************************
align 4
mdio_write:
DEBUGF 1,"Writing MII registers\n"
push eax
call mdio_sync
pop eax
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_PHYSICAL_MGMT
shl al, 3
shr ax, 3
and ax, not MII_CMD_MASK
or ax, MII_CMD_WRITE
shl eax, 2
or eax, 10b ; transition bits
shl eax, 16
mov ax, si
mov esi, eax
mov ecx, 31
.cmd_loop:
mov ax, (1 shl BIT_MGMT_DIR) ; write mii
bt esi, ecx
jnc @f
or al, (1 shl BIT_MGMT_DATA)
@@:
out dx, ax
push eax
in ax, dx ; delay
pop eax
or al, (1 shl BIT_MGMT_CLK) ; write
out dx, ax
in ax, dx ; delay
loop .cmd_loop
ret
;***************************************************************************
; Function
; check_tx_status
; Description
; Checks TxStatus queue.
; Return value
; al - 0 no error was found
; al - 1 error was found TxReset was needed
; Destroyed registers
; eax, ecx, edx
;
;***************************************************************************
align 4
check_tx_status:
DEBUGF 1,"Checking TX status\n"
; clear TxStatus queue
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_TX_STATUS
mov ecx, 31 ; max number of queue entries
.tx_status_loop:
in al, dx
test al, al
jz .finish ; no error
test al, 0x3f
jnz .error
.no_error_found:
; clear current TxStatus entry which advances the next one
xor al, al
out dx, al
loop .tx_status_loop
.finish:
ret
.error:
call tx_reset
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Transmit (vortex) ;;
;; ;;
;; In: pointer to device structure in ebx ;;
;; Out: eax = 0 on success ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
align 16
proc vortex_transmit stdcall bufferptr
spin_lock_irqsave
mov esi, [bufferptr]
DEBUGF 1,"Transmitting packet, buffer:%x, size:%u\n", [bufferptr], [esi + NET_BUFF.length]
lea eax, [esi + NET_BUFF.data]
DEBUGF 1,"To: %x-%x-%x-%x-%x-%x From: %x-%x-%x-%x-%x-%x Type:%x%x\n",\
[eax+00]:2,[eax+01]:2,[eax+02]:2,[eax+03]:2,[eax+04]:2,[eax+05]:2,\
[eax+06]:2,[eax+07]:2,[eax+08]:2,[eax+09]:2,[eax+10]:2,[eax+11]:2,\
[eax+13]:2,[eax+12]:2
cmp [esi + NET_BUFF.length], 1514
ja .error
cmp [esi + NET_BUFF.length], 60
jb .error
call check_tx_status
; switch to register window 7
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW + 7
out dx, ax
; check for master operation in progress
set_io [ebx + device.io_addr], REG_MASTER_STATUS
in ax, dx
test ah, 0x80
jnz .overrun ; no DMA for sending
; program frame address to be sent
set_io [ebx + device.io_addr], REG_MASTER_ADDRESS
mov eax, esi
add eax, [eax + NET_BUFF.offset]
invoke GetPhysAddr
out dx, eax
; program frame length
set_io [ebx + device.io_addr], REG_MASTER_LEN
mov eax, [esi + NET_BUFF.length]
out dx, ax
; start DMA Down
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, (10100b shl 11) + 1 ; StartDMADown
out dx, ax
; Update stats
inc [ebx + device.packets_tx]
mov eax, [esi + NET_BUFF.length]
add dword[ebx + device.bytes_tx], eax
adc dword[ebx + device.bytes_tx + 4], 0
spin_unlock_irqrestore
xor eax, eax
ret
.error:
DEBUGF 2, "TX packet error\n"
inc [ebx + device.packets_tx_err]
invoke NetFree, [bufferptr]
spin_unlock_irqrestore
or eax, -1
ret
.overrun:
DEBUGF 2, "TX overrun\n"
inc [ebx + device.packets_tx_ovr]
invoke NetFree, [bufferptr]
spin_unlock_irqrestore
or eax, -1
ret
endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Transmit (boomerang) ;;
;; ;;
;; In: pointer to device structure in ebx ;;
;; Out: eax = 0 on success ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
align 16
proc boomerang_transmit stdcall bufferptr
spin_lock_irqsave
mov esi, [bufferptr]
DEBUGF 1,"Transmitting packet, buffer:%x, size:%u\n", [bufferptr], [esi + NET_BUFF.length]
lea eax, [esi + NET_BUFF.data]
DEBUGF 1,"To: %x-%x-%x-%x-%x-%x From: %x-%x-%x-%x-%x-%x Type:%x%x\n",\
[eax+00]:2,[eax+01]:2,[eax+02]:2,[eax+03]:2,[eax+04]:2,[eax+05]:2,\
[eax+06]:2,[eax+07]:2,[eax+08]:2,[eax+09]:2,[eax+10]:2,[eax+11]:2,\
[eax+13]:2,[eax+12]:2
cmp [esi + NET_BUFF.length], 1514
ja .error
cmp [esi + NET_BUFF.length], 60
jb .error
call check_tx_status ; Reset TX engine if needed
; calculate descriptor address
mov edi, [ebx + device.curr_tx]
DEBUGF 1,"Previous TX desc: %x\n", edi
add edi, sizeof.tx_desc
lea ecx, [ebx + device.tx_desc_buffer + (NUM_TX_DESC)*sizeof.tx_desc]
cmp edi, ecx
jb @f
lea edi, [ebx + device.tx_desc_buffer] ; Wrap if needed
@@:
DEBUGF 1,"Using TX desc: %x\n", esi
; check DnListPtr
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_DN_LIST_PTR
in eax, dx
; mark if Dn_List_Ptr is cleared
test eax, eax
setz [ebx + device.dn_list_ptr_cleared]
; finish if no more free descriptor is available - FIXME!
; cmp eax, esi
; jz .finish
; update statistics
inc [ebx + device.packets_tx]
mov ecx, [esi + NET_BUFF.length]
add dword [ebx + device.bytes_tx], ecx
adc dword [ebx + device.bytes_tx + 4], 0
; program DPD
and [edi + tx_desc.next_ptr], 0
mov eax, [bufferptr]
mov [edi + tx_desc.realaddr], eax
add eax, [eax + NET_BUFF.offset]
invoke GetPhysAddr
mov [edi + tx_desc.frag_addr], eax
;;; mov ecx, [buffersize]
or ecx, 0x80000000 ; last fragment flag
mov [edi + tx_desc.frag_len], ecx
;;; mov ecx, [buffersize] ; packet size
or ecx, 0x80008000 ; set OWN bit + transmission complete notification flag
; test byte [ebx + device.has_hwcksm], 0xff
; jz @f
; or ecx, (1 shl 26) ; set AddTcpChecksum
;@@:
mov [edi + tx_desc.frame_start_hdr], ecx
DEBUGF 1,"TX desc: lin=%x phys=%x len=%x start hdr=%x\n", [edi+tx_desc.realaddr]:8, [edi+tx_desc.frag_addr]:8, [edi+tx_desc.frag_len]:8, [edi+tx_desc.frame_start_hdr]:8
; calculate physical address of tx descriptor
mov eax, edi
invoke GetPhysAddr
cmp [ebx + device.dn_list_ptr_cleared], 0
je .add_to_list
; write Dn_List_Ptr
DEBUGF 1,"TX desc phys addr=%x\n", eax
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_DN_LIST_PTR
out dx, eax
jmp .finish
.add_to_list:
DEBUGF 1,"Adding To list\n"
push eax
; DnStall
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, ((110b shl 11)+2)
out dx, ax
; wait for DnStall to complete
DEBUGF 1,"Waiting for DnStall\n"
mov ecx, 6000
.wait_for_stall:
in ax, dx ; read REG_INT_STATUS
test ah, 10000b
jz .dnstall_ok
dec ecx
jnz .wait_for_stall
.dnstall_ok:
DEBUGF 1,"DnStall ok!\n"
mov ecx, [ebx + device.curr_tx]
mov [ecx + tx_desc.next_ptr], eax
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_DN_LIST_PTR
in eax, dx
test eax, eax
pop eax
jnz .dnunstall
; if Dn_List_Ptr has been cleared fill it up
DEBUGF 1,"DnList Ptr has been cleared\n"
out dx, eax
.dnunstall:
; DnUnStall
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, ((110b shl 11)+3)
out dx, ax
.finish:
mov [ebx + device.curr_tx], edi
; Update stats
inc [ebx + device.packets_tx]
mov eax, [esi + NET_BUFF.length]
add dword[ebx + device.bytes_tx], eax
adc dword[ebx + device.bytes_tx + 4], 0
spin_unlock_irqrestore
xor eax, eax
ret
.error:
DEBUGF 2, "TX packet error\n"
inc [ebx + device.packets_tx_err]
invoke NetFree, [bufferptr]
spin_unlock_irqrestore
or eax, -1
ret
.overrun:
DEBUGF 2, "TX overrun\n"
inc [ebx + device.packets_tx_ovr]
invoke NetFree, [bufferptr]
spin_unlock_irqrestore
or eax, -1
ret
endp
;---------------------------------
; Write MAC
align 4
write_mac:
DEBUGF 1,"Writing mac\n"
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
; switch to register window 2
mov ax, SELECT_REGISTER_WINDOW+2
out dx, ax
; write MAC addres back into the station address registers
set_io [ebx + device.io_addr], REG_STATION_ADDRESS_LO
lea esi, [ebx + device.mac]
outsw
inc dx
inc dx
outsw
inc dx
inc dx
outsw
;----------------------------
; Read MAC
align 4
read_mac:
DEBUGF 1,"Reading MAC\n"
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
; switch to register window 2
mov ax, SELECT_REGISTER_WINDOW+2
out dx, ax
; Read the MAC and write it to device.mac
set_io [ebx + device.io_addr], REG_STATION_ADDRESS_LO
lea edi, [ebx + device.mac]
insw
inc dx
inc dx
insw
inc dx
inc dx
insw
DEBUGF 1,"%x-%x-%x-%x-%x-%x\n", \
[ebx + device.mac+0]:2,[ebx + device.mac+1]:2,[ebx + device.mac+2]:2,\
[ebx + device.mac+3]:2,[ebx + device.mac+4]:2,[ebx + device.mac+5]:2
ret
;------------------------------------
; Read MAC from eeprom
align 4
read_mac_eeprom:
DEBUGF 1,"Reading MAC from eeprom\n"
; read MAC from eeprom and write it to device.mac
mov ecx, 3
.mac_loop:
lea ax, [EEPROM_REG_OEM_NODE_ADDR+ecx-1]
push ecx
call read_eeprom
pop ecx
xchg ah, al
mov word [ebx + device.mac+ecx*2-2], ax
loop .mac_loop
DEBUGF 1,"%x-%x-%x-%x-%x-%x\n",\
[ebx + device.mac+0]:2,[ebx + device.mac+1]:2,[ebx + device.mac+2]:2,\
[ebx + device.mac+3]:2,[ebx + device.mac+4]:2,[ebx + device.mac+5]:2
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Vortex Interrupt handler ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
align 16
int_vortex:
push ebx esi edi
mov ebx, [esp+4*4]
DEBUGF 1,"INT for 0x%x\n", ebx
; TODO? if we are paranoid, we can check that the value from ebx is present in the current device_list
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_INT_STATUS
in ax, dx
and ax, S_5_INTS
jnz .nothing
DEBUGF 1,"Status: %x\n", eax:4
test ax, RxComplete
jz .noRX
set_io [ebx + device.io_addr], 0
.rx_status_loop:
; examine RxStatus
set_io [ebx + device.io_addr], REG_RX_STATUS
in ax, dx
test ax, ax
jz .finish
test ah, 0x80 ; rxIncomplete
jnz .finish
test ah, 0x40
jz .check_length
; discard the top frame received advancing the next one
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, (01000b shl 11)
out dx, ax
jmp .rx_status_loop
.check_length:
and eax, 0x1fff
cmp eax, MAX_ETH_FRAME_SIZE
ja .discard_frame ; frame is too long discard it
.check_dma:
mov ecx, eax
; switch to register window 7
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+7
out dx, ax
; check for master operation in progress
set_io [ebx + device.io_addr], REG_MASTER_STATUS
in ax, dx
test ah, 0x80
jnz .finish
.read_frame:
; program buffer address to read in
push ecx
invoke NetAlloc, MAX_ETH_FRAME_SIZE + NET_BUFF.data
pop ecx
test eax, eax
jz .finish
push .discard_frame
push eax
mov [eax + NET_BUFF.length], ecx
mov [eax + NET_BUFF.device], ebx
mov [eax + NET_BUFF.offset], NET_BUFF.data
invoke GetPhysAddr
add eax, NET_BUFF.data
set_io [ebx + device.io_addr], REG_MASTER_ADDRESS
out dx, eax
; program frame length
set_io [ebx + device.io_addr], REG_MASTER_LEN
mov ax, 1560
out dx, ax
; start DMA Up
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, (10100b shl 11) ; StartDMAUp
out dx, ax
; check for master operation in progress
set_io [ebx + device.io_addr], REG_MASTER_STATUS ; TODO: use timeout and reset after timeout expired
.dma_loop:
in ax, dx
test ah, 0x80
jnz .dma_loop
; registrate the received packet to kernel
jmp [EthInput]
; discard the top frame received
.discard_frame:
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, (01000b shl 11)
out dx, ax
.finish:
.noRX:
test ax, DMADone
jz .noDMA
push ax
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], 12
in ax, dx
test ax, 0x1000
jz .nodmaclear
mov ax, 0x1000
out dx, ax
.nodmaclear:
pop ax
DEBUGF 1, "DMA Done!\n", cx
.noDMA:
.ACK:
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, AckIntr + IntReq + IntLatch
out dx, ax
pop edi esi ebx
xor eax, eax
inc eax
ret
.nothing:
pop edi esi ebx
xor eax, eax
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Boomerang Interrupt handler ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
align 16
int_boomerang:
push ebx esi edi
mov ebx, [esp+4*4]
DEBUGF 1,"INT for 0x%x\n", ebx
; TODO? if we are paranoid, we can check that the value from ebx is present in the current device_list
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_INT_STATUS
in ax, dx
test ax, S_5_INTS
jz .nothing
.got_it:
DEBUGF 1,"Status: %x\n", ax
push ax
; disable all INTS
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, SetIntrEnb
out dx, ax
;--------------------------------------------------------------------------
test word[esp], UpComplete
jz .noRX
push ebx
.receive:
DEBUGF 1,"UpComplete\n"
; check if packet is uploaded
mov esi, [ebx + device.curr_rx]
test byte [esi+rx_desc.pkt_status+1], 0x80 ; upPktComplete
jz .finish
DEBUGF 1, "Current RX desc: %x\n", esi
; packet is uploaded check for any error
.check_error:
test byte [esi + rx_desc.pkt_status+1], 0x40 ; upError
jz .copy_packet_length
DEBUGF 1,"Error in packet\n"
and [esi + rx_desc.pkt_status], 0 ; mark packet as read
jmp .finish
.copy_packet_length:
mov ecx, [esi + rx_desc.pkt_status]
and ecx, 0x1fff
; cmp ecx, MAX_ETH_PKT_SIZE
; jbe .copy_packet
; and [esi+rx_desc.pkt_status], 0
; jmp .finish
; .copy_packet:
DEBUGF 1, "Received %u bytes in buffer %x\n", ecx, [esi + rx_desc.realaddr]:8
push dword .loop ;.finish
mov eax, [esi + rx_desc.realaddr]
push eax
mov [eax + NET_BUFF.length], ecx
mov [eax + NET_BUFF.device], ebx
mov [eax + NET_BUFF.offset], NET_BUFF.data
; update statistics
inc [ebx + device.packets_rx]
add dword [ebx + device.bytes_rx], ecx
adc dword [ebx + device.bytes_rx + 4], 0
; update rx descriptor (Alloc new buffer for next packet)
invoke NetAlloc, MAX_ETH_FRAME_SIZE + NET_BUFF.data
mov [esi + rx_desc.realaddr], eax
invoke GetPhysAddr
add eax, NET_BUFF.data
mov [esi + rx_desc.frag_addr], eax
and [esi + rx_desc.pkt_status], 0
mov [esi + rx_desc.frag_len], MAX_ETH_FRAME_SIZE or (1 shl 31)
; Update rx descriptor pointer
add esi, sizeof.rx_desc
lea ecx, [ebx + device.rx_desc_buffer+(NUM_RX_DESC)*sizeof.rx_desc]
cmp esi, ecx
jb @f
lea esi, [ebx + device.rx_desc_buffer]
@@:
mov [ebx + device.curr_rx], esi
DEBUGF 1, "Next RX desc: %x\n", esi
jmp [EthInput]
.loop:
mov ebx, [esp]
jmp .receive
.finish:
pop ebx
; check if the NIC is in the upStall state
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_UP_PKT_STATUS
in eax, dx
test ah, 0x20 ; UpStalled
jz .noUpUnStall
DEBUGF 1, "upUnStalling\n"
; issue upUnStall command
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, ((11b shl 12)+1) ; upUnStall
out dx, ax
;;;; FIXME: make upunstall work
.noUpUnStall:
.noRX:
test word[esp], DownComplete
jz .noTX
DEBUGF 1, "Downcomplete!\n"
mov ecx, NUM_TX_DESC
lea esi, [ebx + device.tx_desc_buffer]
.txloop:
test [esi+tx_desc.frame_start_hdr], 1 shl 31
jz .maybenext
and [esi+tx_desc.frame_start_hdr], 0
push ecx
invoke NetFree, [esi+tx_desc.realaddr]
pop ecx
.maybenext:
add esi, sizeof.tx_desc
dec ecx
jnz .txloop
.noTX:
pop ax
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_COMMAND
or ax, AckIntr
out dx, ax
set_io [ebx + device.io_addr], REG_INT_STATUS
in ax, dx
test ax, S_5_INTS
jnz .got_it
;re-enable ints
set_io [ebx + device.io_addr], REG_COMMAND
mov ax, SetIntrEnb + S_5_INTS
out dx, ax
pop edi esi ebx
xor eax, eax
inc eax
ret
.nothing:
pop edi esi ebx
xor eax, eax
ret
; End of code
data fixups
end data
include '../peimport.inc'
my_service db '3C59X',0 ; max 16 chars include zero
macro strtbl name, [string]
{
common
label name dword
forward
local label
dd label
forward
label db string, 0
}
strtbl link_str, \
"No valid link type detected", \
"10BASE-T half duplex", \
"10BASE-T full-duplex", \
"100BASE-TX half duplex", \
"100BASE-TX full duplex", \
"100BASE-T4", \
"100BASE-FX", \
"10Mbps AUI", \
"10Mbps COAX (BNC)", \
"miiDevice - not supported"
strtbl hw_str, \
"3c590 Vortex 10Mbps", \
"3c592 EISA 10Mbps Demon/Vortex", \
"3c597 EISA Fast Demon/Vortex", \
"3c595 Vortex 100baseTx", \
"3c595 Vortex 100baseT4", \
"3c595 Vortex 100base-MII", \
"3c900 Boomerang 10baseT", \
"3c900 Boomerang 10Mbps Combo", \
"3c900 Cyclone 10Mbps TPO", \
"3c900 Cyclone 10Mbps Combo", \
"3c900 Cyclone 10Mbps TPC", \
"3c900B-FL Cyclone 10base-FL", \
"3c905 Boomerang 100baseTx", \
"3c905 Boomerang 100baseT4", \
"3c905B Cyclone 100baseTx", \
"3c905B Cyclone 10/100/BNC", \
"3c905B-FX Cyclone 100baseFx", \
"3c905C Tornado", \
"3c980 Cyclone", \
"3c982 Dual Port Server Cyclone", \
"3cSOHO100-TX Hurricane", \
"3c555 Laptop Hurricane", \
"3c556 Laptop Tornado", \
"3c556B Laptop Hurricane", \
"3c575 [Megahertz] 10/100 LAN CardBus", \
"3c575 Boomerang CardBus", \
"3CCFE575BT Cyclone CardBus", \
"3CCFE575CT Tornado CardBus", \
"3CCFE656 Cyclone CardBus", \
"3CCFEM656B Cyclone+Winmodem CardBus", \
"3CXFEM656C Tornado+Winmodem CardBus", \
"3c450 HomePNA Tornado", \
"3c920 Tornado", \
"3c982 Hydra Dual Port A", \
"3c982 Hydra Dual Port B", \
"3c905B-T4", \
"3c920B-EMB-WNM Tornado"
align 4
hw_versions:
dw 0x5900, IS_VORTEX ; 3c590 Vortex 10Mbps
dw 0x5920, IS_VORTEX ; 3c592 EISA 10Mbps Demon/Vortex
dw 0x5970, IS_VORTEX ; 3c597 EISA Fast Demon/Vortex
dw 0x5950, IS_VORTEX ; 3c595 Vortex 100baseTx
dw 0x5951, IS_VORTEX ; 3c595 Vortex 100baseT4
dw 0x5952, IS_VORTEX ; 3c595 Vortex 100base-MII
dw 0x9000, IS_BOOMERANG ; 3c900 Boomerang 10baseT
dw 0x9001, IS_BOOMERANG ; 3c900 Boomerang 10Mbps Combo
dw 0x9004, IS_CYCLONE or HAS_NWAY or HAS_HWCKSM ; 3c900 Cyclone 10Mbps TPO
dw 0x9005, IS_CYCLONE or HAS_HWCKSM ; 3c900 Cyclone 10Mbps Combo
dw 0x9006, IS_CYCLONE or HAS_HWCKSM ; 3c900 Cyclone 10Mbps TPC
dw 0x900A, IS_CYCLONE or HAS_HWCKSM ; 3c900B-FL Cyclone 10base-FL
dw 0x9050, IS_BOOMERANG or HAS_MII ; 3c905 Boomerang 100baseTx
dw 0x9051, IS_BOOMERANG or HAS_MII ; 3c905 Boomerang 100baseT4
dw 0x9055, IS_CYCLONE or HAS_NWAY or HAS_HWCKSM or EXTRA_PREAMBLE ; 3c905B Cyclone 100baseTx
dw 0x9058, IS_CYCLONE or HAS_NWAY or HAS_HWCKSM ; 3c905B Cyclone 10/100/BNC
dw 0x905A, IS_CYCLONE or HAS_HWCKSM ; 3c905B-FX Cyclone 100baseFx
dw 0x9200, IS_TORNADO or HAS_NWAY or HAS_HWCKSM ; 3c905C Tornado
dw 0x9800, IS_CYCLONE or HAS_NWAY or HAS_HWCKSM ; 3c980 Cyclone
dw 0x9805, IS_TORNADO or HAS_NWAY or HAS_HWCKSM ; 3c982 Dual Port Server Cyclone
dw 0x7646, IS_CYCLONE or HAS_NWAY or HAS_HWCKSM ; 3cSOHO100-TX Hurricane
dw 0x5055, IS_CYCLONE or EEPROM_8BIT or HAS_HWCKSM ; 3c555 Laptop Hurricane
dw 0x6055, IS_TORNADO or HAS_NWAY or EEPROM_8BIT or HAS_CB_FNS or INVERT_MII_PWR or HAS_HWCKSM ; 3c556 Laptop Tornado
dw 0x6056, IS_TORNADO or HAS_NWAY or EEPROM_OFFSET or HAS_CB_FNS or INVERT_MII_PWR or HAS_HWCKSM ; 3c556B Laptop Hurricane
dw 0x5b57, IS_BOOMERANG or HAS_MII or EEPROM_8BIT ; 3c575 [Megahertz] 10/100 LAN CardBus
dw 0x5057, IS_BOOMERANG or HAS_MII or EEPROM_8BIT ; 3c575 Boomerang CardBus
dw 0x5157, IS_CYCLONE or HAS_NWAY or HAS_CB_FNS or EEPROM_8BIT or INVERT_LED_PWR or HAS_HWCKSM ; 3CCFE575BT Cyclone CardBus
dw 0x5257, IS_TORNADO or HAS_NWAY or HAS_CB_FNS or EEPROM_8BIT or INVERT_MII_PWR or MAX_COLLISION_RESET or HAS_HWCKSM ; 3CCFE575CT Tornado CardBus
dw 0x6560, IS_CYCLONE or HAS_NWAY or HAS_CB_FNS or EEPROM_8BIT or INVERT_MII_PWR or INVERT_LED_PWR or HAS_HWCKSM ; 3CCFE656 Cyclone CardBus
dw 0x6562, IS_CYCLONE or HAS_NWAY or HAS_CB_FNS or EEPROM_8BIT or INVERT_MII_PWR or INVERT_LED_PWR or HAS_HWCKSM ; 3CCFEM656B Cyclone+Winmodem CardBus
dw 0x6564, IS_TORNADO or HAS_NWAY or HAS_CB_FNS or EEPROM_8BIT or INVERT_MII_PWR or MAX_COLLISION_RESET or HAS_HWCKSM ; 3CXFEM656C Tornado+Winmodem CardBus
dw 0x4500, IS_TORNADO or HAS_NWAY or HAS_HWCKSM ; 3c450 HomePNA Tornado
dw 0x9201, IS_TORNADO or HAS_NWAY or HAS_HWCKSM ; 3c920 Tornado
dw 0x1201, IS_TORNADO or HAS_HWCKSM or HAS_NWAY ; 3c982 Hydra Dual Port A
dw 0x1202, IS_TORNADO or HAS_HWCKSM or HAS_NWAY ; 3c982 Hydra Dual Port B
dw 0x9056, IS_CYCLONE or HAS_NWAY or HAS_HWCKSM or EXTRA_PREAMBLE ; 3c905B-T4
dw 0x9210, IS_TORNADO or HAS_NWAY or HAS_HWCKSM ; 3c920B-EMB-WNM Tornado
HW_VERSIONS_SIZE = $ - hw_versions
include_debug_strings ; All data wich FDO uses will be included here
align 4
devices dd 0
device_list rd MAX_DEVICES ; This list contains all pointers to device structures the driver is handling