1
1
kolibrios-fun/drivers/ethernet/forcedeth.asm

2033 lines
55 KiB
NASM
Raw Normal View History

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2014. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; FORCEDETH.INC ;;
;; ;;
;; Ethernet driver for Kolibri OS ;;
;; ;;
;; Driver for chips of NVIDIA nForce2 ;;
;; References: ;;
;; forcedeth.c - linux driver (etherboot project) ;;
;; ethernet driver template by Mike Hibbett ;;
;; ;;
;; The copyright statement is ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;; Copyright 2008 shurf, ;;
;; cit.utc@gmail.com ;;
;; ;;
;; See file COPYING for details ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
format PE DLL native
entry START
CURRENT_API = 0x0200
COMPATIBLE_API = 0x0100
API_VERSION = (COMPATIBLE_API shl 16) + CURRENT_API
MAX_DEVICES = 16
RBLEN = 0 ; Receive buffer size: 0=4K 1=8k 2=16k 3=32k 4=64k
; FIXME: option 1 and 2 will not allocate buffer correctly causing data loss!
DEBUG = 1
__DEBUG__ = 1
__DEBUG_LEVEL__ = 2
RX_RING = 4
TX_RING = 4
section '.flat' readable writable executable
include '../proc32.inc'
include '../struct.inc'
include '../macros.inc'
include '../fdo.inc'
include '../netdrv.inc'
include '../mii.inc'
;**************************************************************************
; forcedeth Register Definitions
;**************************************************************************
PCI_DEVICE_ID_NVIDIA_NVENET_1 = 0x01c3
PCI_DEVICE_ID_NVIDIA_NVENET_2 = 0x0066
PCI_DEVICE_ID_NVIDIA_NVENET_4 = 0x0086
PCI_DEVICE_ID_NVIDIA_NVENET_5 = 0x008c
PCI_DEVICE_ID_NVIDIA_NVENET_3 = 0x00d6
PCI_DEVICE_ID_NVIDIA_NVENET_7 = 0x00df
PCI_DEVICE_ID_NVIDIA_NVENET_6 = 0x00e6
PCI_DEVICE_ID_NVIDIA_NVENET_8 = 0x0056
PCI_DEVICE_ID_NVIDIA_NVENET_9 = 0x0057
PCI_DEVICE_ID_NVIDIA_NVENET_10 = 0x0037
PCI_DEVICE_ID_NVIDIA_NVENET_11 = 0x0038
PCI_DEVICE_ID_NVIDIA_NVENET_12 = 0x0268
PCI_DEVICE_ID_NVIDIA_NVENET_13 = 0x0269
PCI_DEVICE_ID_NVIDIA_NVENET_14 = 0x0372
PCI_DEVICE_ID_NVIDIA_NVENET_15 = 0x0373
UNKSETUP1_VAL = 0x16070f
UNKSETUP2_VAL = 0x16
UNKSETUP3_VAL1 = 0x200010
UNKSETUP4_VAL = 8
UNKSETUP5_BIT31 = (1 shl 31)
UNKSETUP6_VAL = 3
TXRXCTL_RXCHECK = 0x0400
MIISTAT_ERROR = 0x0001
MIISTAT_MASK = 0x000f
MIISTAT_MASK2 = 0x000f
MIICTL_INUSE = 0x08000
MIICTL_WRITE = 0x00400
MIICTL_ADDRSHIFT = 5
MIISPEED_BIT8 = (1 shl 8)
MIIDELAY = 5
IRQ_RX_ERROR = 0x0001
IRQ_RX = 0x0002
IRQ_RX_NOBUF = 0x0004
IRQ_TX_ERROR = 0x0008
IRQ_TX_OK = 0x0010
IRQ_TIMER = 0x0020
IRQ_LINK = 0x0040
IRQ_RX_FORCED = 0x0080
IRQ_TX_FORCED = 0x0100
IRQ_RECOVER_ERROR = 0x8200 ;
IRQMASK_WANTED_2 = IRQ_TX_FORCED + IRQ_LINK + IRQ_RX_ERROR + IRQ_RX + IRQ_TX_OK + IRQ_TX_ERROR
IRQ_RX_ALL = IRQ_RX_ERROR or IRQ_RX or IRQ_RX_NOBUF or IRQ_RX_FORCED
IRQ_TX_ALL = IRQ_TX_ERROR or IRQ_TX_OK or IRQ_TX_FORCED
IRQ_OTHER = IRQ_LINK or IRQ_TIMER or IRQ_RECOVER_ERROR
IRQSTAT_MASK = 0x1ff
TXRXCTL_KICK = 0x0001
TXRXCTL_BIT1 = 0x0002
TXRXCTL_BIT2 = 0x0004
TXRXCTL_IDLE = 0x0008
TXRXCTL_RESET = 0x0010
TXRXCTL_RXCHECK = 0x0400
MCASTADDRA_FORCE = 0x01
MAC_RESET_ASSERT = 0x0F3
MISC1_HD = 0x02
MISC1_FORCE = 0x3b0f3c
PFF_ALWAYS = 0x7F0008
PFF_PROMISC = 0x80
PFF_MYADDR = 0x20
OFFLOAD_HOMEPHY = 0x601
OFFLOAD_NORMAL = 4096 shl RBLEN
RNDSEED_MASK = 0x00ff
RNDSEED_FORCE = 0x7f00
RNDSEED_FORCE2 = 0x2d00
RNDSEED_FORCE3 = 0x7400
; POLL_DEFAULT is the interval length of the timer source on the nic
; POLL_DEFAULT=97 would result in an interval length of 1 ms
POLL_DEFAULT = 970
ADAPTCTL_START = 0x02
ADAPTCTL_LINKUP = 0x04
ADAPTCTL_PHYVALID = 0x40000
ADAPTCTL_RUNNING = 0x100000
ADAPTCTL_PHYSHIFT = 24
WAKEUPFLAGS_VAL = 0x7770
POWERSTATE_POWEREDUP = 0x8000
POWERSTATE_VALID = 0x0100
POWERSTATE_MASK = 0x0003
POWERSTATE_D0 = 0x0000
POWERSTATE_D1 = 0x0001
POWERSTATE_D2 = 0x0002
POWERSTATE_D3 = 0x0003
POWERSTATE2_POWERUP_MASK = 0x0F11
POWERSTATE2_POWERUP_REV_A3= 0x0001
RCVCTL_START = 0x01
RCVSTAT_BUSY = 0x01
XMITCTL_START = 0x01
LINKSPEED_FORCE = 0x10000
LINKSPEED_10 = 1000
LINKSPEED_100 = 100
LINKSPEED_1000 = 50
RINGSZ_TXSHIFT = 0
RINGSZ_RXSHIFT = 16
LPA_1000FULL = 0x0800
; Link partner ability register.
LPA_SLCT = 0x001f ; Same as advertise selector
LPA_10HALF = 0x0020 ; Can do 10mbps half-duplex
LPA_10FULL = 0x0040 ; Can do 10mbps full-duplex
LPA_100HALF = 0x0080 ; Can do 100mbps half-duplex
LPA_100FULL = 0x0100 ; Can do 100mbps full-duplex
LPA_100BASE4 = 0x0200 ; Can do 100mbps 4k packets
LPA_RESV = 0x1c00 ; Unused...
LPA_RFAULT = 0x2000 ; Link partner faulted
LPA_LPACK = 0x4000 ; Link partner acked us
LPA_NPAGE = 0x8000 ; Next page bit
MII_READ = (-1)
MII_PHYSID1 = 0x02 ; PHYS ID 1
MII_PHYSID2 = 0x03 ; PHYS ID 2
MII_BMCR = 0x00 ; Basic mode control register
MII_BMSR = 0x01 ; Basic mode status register
MII_ADVERTISE = 0x04 ; Advertisement control reg
MII_LPA = 0x05 ; Link partner ability reg
MII_SREVISION = 0x16 ; Silicon revision
MII_RESV1 = 0x17 ; Reserved...
MII_NCONFIG = 0x1c ; Network interface config
; PHY defines
PHY_OUI_MARVELL = 0x5043
PHY_OUI_CICADA = 0x03f1
PHYID1_OUI_MASK = 0x03ff
PHYID1_OUI_SHFT = 6
PHYID2_OUI_MASK = 0xfc00
PHYID2_OUI_SHFT = 10
PHY_INIT1 = 0x0f000
PHY_INIT2 = 0x0e00
PHY_INIT3 = 0x01000
PHY_INIT4 = 0x0200
PHY_INIT5 = 0x0004
PHY_INIT6 = 0x02000
PHY_GIGABIT = 0x0100
PHY_TIMEOUT = 0x1
PHY_ERROR = 0x2
PHY_100 = 0x1
PHY_1000 = 0x2
PHY_HALF = 0x100
PHY_RGMII = 0x10000000
; desc_ver values:
; This field has two purposes:
; - Newer nics uses a different ring layout. The layout is selected by
; comparing np->desc_ver with DESC_VER_xy.
; - It contains bits that are forced on when writing to TxRxControl.
DESC_VER_1 = 0x0
DESC_VER_2 = (0x02100 or TXRXCTL_RXCHECK)
NV_TX_LASTPACKET = (1 shl 16)
NV_TX_RETRYERROR = (1 shl 19)
NV_TX_LASTPACKET1 = (1 shl 24)
NV_TX_DEFERRED = (1 shl 26)
NV_TX_CARRIERLOST = (1 shl 27)
NV_TX_LATECOLLISION = (1 shl 28)
NV_TX_UNDERFLOW = (1 shl 29)
NV_TX_ERROR = (1 shl 30)
NV_TX_VALID = (1 shl 31)
NV_TX2_LASTPACKET = (1 shl 29)
NV_TX2_RETRYERROR = (1 shl 18)
NV_TX2_LASTPACKET1 = (1 shl 23)
NV_TX2_DEFERRED = (1 shl 25)
NV_TX2_CARRIERLOST = (1 shl 26)
NV_TX2_LATECOLLISION = (1 shl 27)
NV_TX2_UNDERFLOW = (1 shl 28)
; error and valid are the same for both
NV_TX2_ERROR = (1 shl 30)
NV_TX2_VALID = (1 shl 31)
NV_RX_DESCRIPTORVALID = (1 shl 16)
NV_RX_AVAIL = (1 shl 31)
NV_RX2_DESCRIPTORVALID = (1 shl 29)
FLAG_MASK_V1 = 0xffff0000
FLAG_MASK_V2 = 0xffffc000
LEN_MASK_V1 = (0xffffffff xor FLAG_MASK_V1)
LEN_MASK_V2 = (0xffffffff xor FLAG_MASK_V2)
; Miscelaneous hardware related defines:
NV_PCI_REGSZ_VER1 = 0x270
NV_PCI_REGSZ_VER2 = 0x604
; various timeout delays: all in usec
NV_TXRX_RESET_DELAY = 4
NV_TXSTOP_DELAY1 = 10
NV_TXSTOP_DELAY1MAX = 500000
NV_TXSTOP_DELAY2 = 100
NV_RXSTOP_DELAY1 = 10
NV_RXSTOP_DELAY1MAX = 500000
NV_RXSTOP_DELAY2 = 100
NV_SETUP5_DELAY = 5
NV_SETUP5_DELAYMAX = 50000
NV_POWERUP_DELAY = 5
NV_POWERUP_DELAYMAX = 5000
NV_MIIBUSY_DELAY = 50
NV_MIIPHY_DELAY = 10
NV_MIIPHY_DELAYMAX = 10000
NV_MAC_RESET_DELAY = 64
NV_WAKEUPPATTERNS = 5
NV_WAKEUPMASKENTRIES = 4
struct TxDesc
PacketBuffer dd ?
FlagLen dd ?
ends
struct RxDesc
PacketBuffer dd ?
FlagLen dd ?
ends
struct device ETH_DEVICE
pci_bus dd ?
pci_dev dd ?
mmio_addr dd ?
vendor_id dw ?
device_id dw ?
txflags dd ?
desc_ver dd ?
irqmask dd ?
wolenabled dd ?
in_shutdown dd ?
cur_rx dd ?
cur_tx dd ?
last_tx dd ?
phyaddr dd ?
phy_oui dd ?
gigabit dd ?
needs_mac_reset dd ?
linkspeed dd ?
duplex dd ?
nocable dd ?
rb 0x100 - ($ and 0xff)
tx_ring rd (TX_RING * sizeof.TxDesc) /4*2
rb 0x100 - ($ and 0xff)
rx_ring rd (RX_RING * sizeof.RxDesc) /4*2
ends
virtual at edi
IrqStatus dd ?
IrqMask dd ?
UnknownSetupReg6 dd ?
PollingInterval dd ?
end virtual
virtual at edi + 0x3c
MacReset dd ?
end virtual
virtual at edi + 0x80
Misc1 dd ?
TransmitterControl dd ?
TransmitterStatus dd ?
PacketFilterFlags dd ?
OffloadConfig dd ?
ReceiverControl dd ?
ReceiverStatus dd ?
RandomSeed dd ?
UnknownSetupReg1 dd ?
UnknownSetupReg2 dd ?
MacAddrA dd ?
MacAddrB dd ?
MulticastAddrA dd ?
MulticastAddrB dd ?
MulticastMaskA dd ?
MulticastMaskB dd ?
PhyInterface dd ?
end virtual
virtual at edi + 0x100
TxRingPhysAddr dd ?
RxRingPhysAddr dd ?
RingSizes dd ?
UnknownTransmitterReg dd ?
LinkSpeed dd ?
end virtual
virtual at edi + 0x130
UnknownSetupReg5 dd ?
end virtual
virtual at edi + 0x13c
UnknownSetupReg3 dd ?
end virtual
virtual at edi + 0x144
TxRxControl dd ?
end virtual
virtual at edi + 0x180
MIIStatus dd ?
UnknownSetupReg4 dd ?
AdapterControl dd ?
MIISpeed dd ?
MIIControl dd ?
MIIData dd ?
end virtual
virtual at edi + 0x200
WakeUpFlags dd ?
end virtual
virtual at edi + 0x26c
PowerState dd ?
end virtual
virtual at edi + 0x600
PowerState2 dd ?
end virtual
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; 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) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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 arent supported for this card yet
; check if the device is already listed
mov esi, device_list
mov ecx, [devices]
test ecx, ecx
jz .firstdevice
; 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] ; compare with pci and device num in device list (notice the usage of word instead of byte)
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], transmit
mov [ebx + device.unload], .fail
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
DEBUGF 1,"Hooking into device, dev:%x, bus:%x\n", [ebx + device.pci_dev], [ebx + device.pci_bus]
; 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 ; (IRQ handler uses this list to find device)
inc [devices] ;
mov [ebx + device.type], NET_TYPE_ETH
invoke NetRegDev
cmp eax, -1
je .destroy
ret
; If the device was already loaded, find the device number and return it in eax
.find_devicenum:
DEBUGF 1,"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 1,"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:
ret
;------------------------------------------------------
endp
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
;; ;;
;; Actual Hardware dependent code starts here ;;
;; ;;
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
;***************************************************************************
; Function
; probe
; Description
; Searches for an ethernet card, enables it and clears the rx buffer
;
;***************************************************************************
align 4
probe:
DEBUGF 1,"probe\n"
mov [ebx + device.needs_mac_reset], 0
; Make the device a bus master and enable response in I/O space
invoke PciRead32, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.command
or al, PCI_CMD_MASTER + PCI_CMD_PIO ; + PCI_CMD_MMIO
invoke PciWrite32, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.command, eax
; Adjust PCI latency to be at least 32
invoke PciRead8, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.max_latency
cmp al, 32
jae @f
mov al, 32
invoke PciWrite8, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.max_latency, eax
@@:
; Now, it's time to find the base mmio addres of the PCI device
stdcall PCI_find_mmio32, [ebx + device.pci_bus], [ebx + device.pci_dev] ; returns in eax
DEBUGF 1,"mmio_addr= 0x%x\n", eax
; Create virtual mapping of the physical memory
invoke MapIoMem, eax, 10000h, PG_SW + PG_NOCACHE
test eax, eax
jz fail
mov [ebx + device.mmio_addr], eax
DEBUGF 1,"mapped mmio_addr= 0x%x\n", eax
; Read PCI vendor/device ID
invoke PciRead32, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.vendor_id
mov dword[ebx + device.vendor_id], eax
DEBUGF 1,"vendor = 0x%x\n", [ebx + device.vendor_id]:4
DEBUGF 1,"device = 0x%x\n", [ebx + device.device_id]:4
;-------------------------------------
; handle different descriptor versions
mov [ebx + device.desc_ver], DESC_VER_1
mov ax, [ebx + device.device_id]
cmp ax, PCI_DEVICE_ID_NVIDIA_NVENET_1
je .ver1
cmp ax, PCI_DEVICE_ID_NVIDIA_NVENET_2
je .ver1
cmp ax, PCI_DEVICE_ID_NVIDIA_NVENET_3
je .ver1
mov [ebx + device.desc_ver], DESC_VER_2
.ver1:
call read_mac
; disable WOL
mov [WakeUpFlags], 0
mov [ebx + device.wolenabled], 0
mov [ebx + device.txflags], (NV_TX2_LASTPACKET or NV_TX2_VALID)
cmp [ebx + device.desc_ver], DESC_VER_1
jne @f
mov [ebx + device.txflags], (NV_TX_LASTPACKET or NV_TX_VALID)
@@:
; BEGIN of switch (pci->dev_id)
cmp [ebx + device.device_id], 0x01C3
jne .not_0x01c3
; nforce
mov [ebx + device.irqmask], (IRQMASK_WANTED_2 or IRQ_TIMER) ;;; Was 0
jmp .find_phy
.not_0x01c3:
cmp [ebx + device.device_id], 0x0066
je @f
cmp [ebx + device.device_id], 0x00D6
jne .not_0x0066
@@:
mov [ebx + device.irqmask], (IRQMASK_WANTED_2 or IRQ_TIMER) ;;;; was 0
cmp [ebx + device.desc_ver], DESC_VER_1
jne @f
or [ebx + device.txflags], NV_TX_LASTPACKET1
jmp .find_phy
@@:
or [ebx + device.txflags], NV_TX2_LASTPACKET1
jmp .find_phy
.not_0x0066:
cmp [ebx + device.device_id], 0x0086
je @f
cmp [ebx + device.device_id], 0x008c
je @f
cmp [ebx + device.device_id], 0x00e6
je @f
cmp [ebx + device.device_id], 0x00df
je @f
cmp [ebx + device.device_id], 0x0056
je @f
cmp [ebx + device.device_id], 0x0057
je @f
cmp [ebx + device.device_id], 0x0037
je @f
cmp [ebx + device.device_id], 0x0038
jne .not_0x0086
@@:
mov [ebx + device.irqmask], (IRQMASK_WANTED_2 or IRQ_TIMER) ;;; was 0
cmp [ebx + device.desc_ver], DESC_VER_1
jne @f
or [ebx + device.txflags], NV_TX_LASTPACKET1
jmp .find_phy
@@:
or [ebx + device.txflags], NV_TX2_LASTPACKET1
jmp .find_phy
.not_0x0086:
; cmp word [device_id], 0x0268
; je @f
; cmp word [device_id], 0x0269
; je @f
; cmp word [device_id], 0x0372
; je @f
; cmp word [device_id], 0x0373
; je @f
; jmp .default_switch
;@@:
cmp [ebx + device.device_id], 0x0268
jb .undefined
; Get device revision
invoke PciRead8, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header.revision_id
mov edi, [ebx + device.mmio_addr] ;;;;;
; take phy and nic out of low power mode
mov ecx, [PowerState2]
and ecx, not POWERSTATE2_POWERUP_MASK
cmp [ebx + device.device_id], PCI_DEVICE_ID_NVIDIA_NVENET_12
je @f
cmp [ebx + device.device_id], PCI_DEVICE_ID_NVIDIA_NVENET_13
jne .set_powerstate
@@:
cmp al, 0xA3
jb .set_powerstate
or ecx, POWERSTATE2_POWERUP_REV_A3
.set_powerstate:
mov [PowerState2], ecx
; DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ
mov [ebx + device.irqmask], (IRQMASK_WANTED_2 or IRQ_TIMER) ; was 0
mov [ebx + device.needs_mac_reset], 1
cmp [ebx + device.desc_ver], DESC_VER_1
jne @f
or [ebx + device.txflags], NV_TX_LASTPACKET1
jmp .find_phy
@@:
cmp [ebx + device.desc_ver], DESC_VER_2
jne .undefined
or [ebx + device.txflags], NV_TX2_LASTPACKET1
jmp .find_phy
.undefined:
DEBUGF 2,"Your card was undefined in this driver.\n"
or eax, -1
ret
; Find a suitable phy
; Start with address 1 to 31, then do 0, then fail
.find_phy:
xor edx, edx
.phy_loop:
inc edx
and edx, 0x1f ; phyaddr = i & 0x1f
mov eax, MII_PHYSID1
mov ecx, MII_READ
call mii_rw ; EDX - addr, EAX - miireg, ECX - value
cmp eax, 0x0000ffff
je .try_next
test eax, 0x80000000
jnz .try_next
mov esi, eax
mov eax, MII_PHYSID2
mov ecx, MII_READ
call mii_rw
cmp eax, 0x0000ffff
je .try_next
test eax, 0x80000000
jnz .try_next
jmp .got_it
.try_next:
test edx, edx
jnz .phy_loop
; PHY in isolate mode? No phy attached and user wants to test loopback?
; Very odd, but can be correct.
DEBUGF 2,"Could not find a valid PHY.\n"
jmp .no_phy
.got_it:
and esi, PHYID1_OUI_MASK
shl esi, PHYID1_OUI_SHFT
and eax, PHYID2_OUI_MASK
shr eax, PHYID2_OUI_SHFT
or eax, esi
mov [ebx + device.phyaddr], edx
mov [ebx + device.phy_oui], eax
DEBUGF 1,"Found PHY with OUI:0x%x at address:0x%x\n", eax, edx
call phy_init
.no_phy:
cmp [ebx + device.needs_mac_reset], 0
je @f
call mac_reset
@@:
;***************************************************************************
; Function
; reset
; Description
; Place the chip (ie, the ethernet card) into a virgin state
; No inputs
; All registers destroyed
;
;***************************************************************************
reset:
DEBUGF 1,"Resetting\n"
invoke PciRead8, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.interrupt_line
movzx eax, al
invoke AttachIntHandler, eax, int_handler, ebx
test eax, eax
jnz @f
DEBUGF 2,"Could not attach int handler!\n"
or eax, -1
ret
@@:
; erase previous misconfiguration
mov edi, [ebx + device.mmio_addr]
mov [MulticastAddrA], MCASTADDRA_FORCE
mov [MulticastAddrB], 0
mov [MulticastMaskA], 0
mov [MulticastMaskB], 0
mov [PacketFilterFlags], 0
mov [TransmitterControl], 0
mov [ReceiverControl], 0
mov [AdapterControl], 0
; initialize descriptor rings
call init_ring
mov [LinkSpeed], 0
mov [UnknownTransmitterReg], 0
call txrx_reset
mov [UnknownSetupReg6], 0
mov [ebx + device.in_shutdown], 0
; give hw rings
lea eax, [ebx + device.rx_ring]
invoke GetPhysAddr
mov [RxRingPhysAddr], eax
lea eax, [ebx + device.tx_ring]
invoke GetPhysAddr
mov [TxRingPhysAddr], eax
mov [RingSizes], (((RX_RING - 1) shl RINGSZ_RXSHIFT) + ((TX_RING - 1) shl RINGSZ_TXSHIFT))
;
mov [ebx + device.linkspeed], (LINKSPEED_FORCE or LINKSPEED_10)
mov [ebx + device.duplex], 0
mov [LinkSpeed], (LINKSPEED_FORCE or LINKSPEED_10)
mov [UnknownSetupReg3], UNKSETUP3_VAL1
mov eax, [ebx + device.desc_ver]
mov [TxRxControl], eax
call pci_push
or eax, TXRXCTL_BIT1
mov [TxRxControl], eax
stdcall reg_delay, UnknownSetupReg5-edi, UNKSETUP5_BIT31, UNKSETUP5_BIT31, NV_SETUP5_DELAY, NV_SETUP5_DELAYMAX, 0
mov [UnknownSetupReg4], 0
mov [MIIStatus], MIISTAT_MASK2
;
mov [Misc1], (MISC1_FORCE or MISC1_HD)
mov eax, [TransmitterStatus]
mov [TransmitterStatus], eax
mov [PacketFilterFlags], PFF_ALWAYS
mov [OffloadConfig], OFFLOAD_NORMAL
mov eax, [ReceiverStatus]
mov [ReceiverStatus], eax
; set random seed
push ebx
invoke GetTimerTicks ; bad idea, driver is started at system startup in 90% of cases..
pop ebx
mov edi, [ebx + device.mmio_addr]
and eax, RNDSEED_MASK
or eax, RNDSEED_FORCE
mov [RandomSeed], eax
mov [UnknownSetupReg1], UNKSETUP1_VAL
mov [UnknownSetupReg2], UNKSETUP2_VAL
mov [PollingInterval], POLL_DEFAULT
mov [UnknownSetupReg6], UNKSETUP6_VAL
mov eax, [ebx + device.phyaddr]
shl eax, ADAPTCTL_PHYSHIFT
or eax, (ADAPTCTL_PHYVALID or ADAPTCTL_RUNNING)
mov [AdapterControl], eax
mov [MIISpeed], (MIISPEED_BIT8 or MIIDELAY)
mov [UnknownSetupReg4], UNKSETUP4_VAL
mov [WakeUpFlags], WAKEUPFLAGS_VAL
or [PowerState], POWERSTATE_POWEREDUP
call pci_push
mov esi, 10
invoke Sleep
or [PowerState], POWERSTATE_VALID
mov [IrqMask], 0
;;; ; ??? Mask RX interrupts
mov [IrqMask], IRQ_RX_ALL + IRQ_TX_ALL
;;; ; ??? Mask TX interrupts
;;; mov [IrqMask], IRQ_TX_ALL
;;; ; ??? Mask OTHER interrupts
;;; mov [IrqMask], IRQ_OTHER_ALL
call pci_push
mov [MIIStatus], MIISTAT_MASK2
mov [IrqStatus], IRQSTAT_MASK
call pci_push
mov [MulticastAddrA], MCASTADDRA_FORCE
mov [MulticastAddrB], 0
mov [MulticastMaskA], 0
mov [MulticastMaskB], 0
mov [PacketFilterFlags], (PFF_ALWAYS or PFF_MYADDR)
call set_multicast
; One manual link speed update: Interrupts are enabled, future link
; speed changes cause interrupts and are handled by nv_link_irq().
mov eax, [MIIStatus]
mov [MIIStatus], MIISTAT_MASK
DEBUGF 1,"startup: got 0x%x\n", eax
call update_linkspeed
mov [TransmitterControl], XMITCTL_START ; start TX
call pci_push
mov [ebx + device.nocable], 0
test eax, eax
jnz .return
DEBUGF 1,"no link during initialization.\n"
mov [ebx + device.nocable], 1
.return:
xor eax, eax ; Indicate that we have successfully reset the card
mov [ebx + device.mtu], 1514 ;;; FIXME
ret
fail:
or eax, -1
ret
;--------------------------------------------------------
;
; MII_RW
;
; read/write a register on the PHY.
; Caller must guarantee serialization
; Input: EAX - miireg, EDX - phy addr, ECX - value to write (or -1 to read)
; Output: EAX - retval (lower 16 bits)
;
;--------------------------------------------------------
mii_rw:
DEBUGF 1,"mii_rw: 0x%x to reg %d at PHY %d\n", ecx, eax, edx
push edx
mov edi, [ebx + device.mmio_addr]
; Check if MII interface is busy
mov [MIIStatus], MIISTAT_MASK
@@:
test [MIIControl], MIICTL_INUSE
jz @f
mov [MIIControl], MIICTL_INUSE
DEBUGF 1,"mii_rw: in use!\n"
pusha
mov esi, NV_MIIBUSY_DELAY
invoke Sleep
popa
jmp @r
@@:
; Set the address we want to access
shl edx, MIICTL_ADDRSHIFT
or edx, eax
; When writing, write the data first.
cmp ecx, MII_READ
je @f
mov [MIIData], ecx
or edx, MIICTL_WRITE
@@:
mov [MIIControl], edx
; Wait for read/write to complete
stdcall reg_delay, MIIControl-edi, MIICTL_INUSE, 0, NV_MIIPHY_DELAY, NV_MIIPHY_DELAYMAX, 0
test eax, eax
jz @f
DEBUGF 1,"mii_rw timed out.\n"
or eax, -1
jmp .return
@@:
cmp ecx, MII_READ
je .read
; it was a write operation - fewer failures are detectable
DEBUGF 1,"mii_rw write: ok\n"
xor eax, eax
jmp .return
.read:
mov eax, [MIIStatus]
test eax, MIISTAT_ERROR
jz @f
DEBUGF 1,"mii read: failed.\n"
or eax, -1
jmp .return
@@:
mov eax, [MIIData]
DEBUGF 1,"mii read: 0x%x.\n", eax
.return:
pop edx
ret
; Input: offset:word, mask:dword, target:dword, delay:word, delaymax:word, msg:dword
; Output: EAX - 0|1
proc reg_delay, offset:dword, mask:dword, target:dword, delay:dword, delaymax:dword, msg:dword
; DEBUGF 1,"reg_delay\n"
push esi
call pci_push
.loop:
mov esi, [delay]
invoke Sleep
mov eax, [delaymax]
sub eax, [delay]
mov [delaymax], eax
cmp eax, 0
jl .fail
mov eax, [offset]
mov eax, [edi + eax]
and eax, [mask]
cmp eax, [target]
jne .loop
pop esi
xor eax, eax
ret
.fail:
pop esi
xor eax, eax
inc eax
ret
endp
; Input: none
; Output: EAX - result (0 = OK, other = error)
phy_init:
push ebx ecx
; set advertise register
mov edx, [ebx + device.phyaddr]
mov eax, MII_ADVERTISE
mov ecx, MII_READ
call mii_rw
or eax, (ADVERTISE_10HALF or ADVERTISE_10FULL or ADVERTISE_100HALF or ADVERTISE_100FULL or 0x800 or 0x400)
mov ecx, eax
mov eax, MII_ADVERTISE
call mii_rw
test eax, eax
jz @f
DEBUGF 2,"phy write to advertise failed.\n"
mov eax, PHY_ERROR
jmp .return
@@:
; get phy interface type
mov edi, [ebx + device.mmio_addr]
mov eax, [PhyInterface]
DEBUGF 1,"phy interface type = 0x%x\n", eax:8
; see if gigabit phy
mov eax, MII_BMSR
mov ecx, MII_READ
call mii_rw
test eax, PHY_GIGABIT
jnz .gigabit
mov [ebx + device.gigabit], 0
jmp .next_if
.gigabit:
mov [ebx + device.gigabit], PHY_GIGABIT
mov eax, MII_CTRL1000
mov ecx, MII_READ
call mii_rw
and eax, (not ADVERTISE_1000HALF)
test [PhyInterface], PHY_RGMII
jz @f
or eax, ADVERTISE_1000FULL
jmp .next
@@:
and eax, (not ADVERTISE_1000FULL)
.next:
mov ecx, eax
mov eax, MII_CTRL1000
call mii_rw
test eax, eax
jz .next_if
DEBUGF 2,"phy init failed.\n"
mov eax, PHY_ERROR
jmp .return
.next_if:
call phy_reset
test eax, eax
jz @f
DEBUGF 2,"phy reset failed.\n"
mov eax, PHY_ERROR
jmp .return
@@:
; phy vendor specific configuration
cmp [ebx + device.phy_oui], PHY_OUI_CICADA
jne .next_if2
test [PhyInterface], PHY_RGMII
jz .next_if2
mov eax, MII_RESV1
mov ecx, MII_READ
call mii_rw
and eax, (not (PHY_INIT1 or PHY_INIT2))
or eax, (PHY_INIT3 or PHY_INIT4)
mov ecx, eax
mov eax, MII_RESV1
call mii_rw
test eax, eax
jz @f
DEBUGF 2,"phy init failed.\n"
mov eax, PHY_ERROR
jmp .return
@@:
mov eax, MII_NCONFIG
mov ecx, MII_READ
call mii_rw
or eax, PHY_INIT5
mov ecx, eax
mov eax, MII_NCONFIG
call mii_rw
test eax, eax
jz .next_if2
DEBUGF 2,"phy init failed.\n"
mov eax, PHY_ERROR
jmp .return
.next_if2:
cmp [ebx + device.phy_oui], PHY_OUI_CICADA
jne .restart
mov eax, MII_SREVISION
mov ecx, MII_READ
call mii_rw
or eax, PHY_INIT6
mov ecx, eax
mov eax, MII_SREVISION
call mii_rw
test eax, eax
jz .restart
DEBUGF 2,"phy init failed.\n"
jmp .return
.restart:
; restart auto negotiation
mov eax, MII_BMCR
mov ecx, MII_READ
call mii_rw
or eax, (BMCR_ANRESTART or BMCR_ANENABLE)
mov ecx, eax
mov eax, MII_BMCR
call mii_rw
test eax, eax
jz .ok
mov eax, PHY_ERROR
jmp .return
.ok:
xor eax, eax
.return:
pop ecx ebx
ret
; Input: none
; Output: EAX - result (0 = OK, other = error)
phy_reset:
DEBUGF 1,"phy_reset\n"
push ebx ecx edx
mov edx, [ebx + device.phyaddr]
mov eax, MII_BMCR
mov ecx, MII_READ
call mii_rw
or eax, BMCR_RESET
push eax
mov ecx, eax
mov eax, MII_BMCR
call mii_rw
test eax, eax
jz @f
pop eax
mov eax, 0xffffffff
jmp .return
@@:
pop eax
mov esi, 500
invoke Sleep
; must wait till reset is deasserted
mov esi, 100 ; FIXME: 100 tries seem excessive
.while_loop:
test eax, BMCR_RESET
jz .while_loop_exit
push esi
mov esi, 10
invoke Sleep
pop esi
mov eax, MII_BMCR
mov ecx, MII_READ
call mii_rw
dec esi
jnz .while_loop
mov eax, 0xffffffff
jmp .return
.while_loop_exit:
xor eax, eax
.return:
pop edx ecx ebx
ret
align 4
pci_push:
push eax
mov eax, [edi]
pop eax
ret
align 4
mac_reset:
push esi edi
DEBUGF 1,"mac_reset.\n"
mov edi, [ebx + device.mmio_addr]
mov eax, [ebx + device.desc_ver]
or eax, (TXRXCTL_BIT2 or TXRXCTL_RESET)
mov [TxRxControl], eax
call pci_push
mov [MacReset], MAC_RESET_ASSERT
call pci_push
mov esi, NV_MAC_RESET_DELAY
invoke Sleep
mov [MacReset], 0
call pci_push
mov esi, NV_MAC_RESET_DELAY
invoke Sleep
mov eax, [ebx + device.desc_ver]
or eax, TXRXCTL_BIT2
mov [TxRxControl], eax
call pci_push
pop edi esi
ret
align 4
init_ring:
DEBUGF 1,"init rings\n"
push eax esi ecx
mov [ebx + device.cur_tx], 0
mov [ebx + device.last_tx], 0
mov ecx, TX_RING
lea esi, [ebx + device.tx_ring]
.tx_loop:
mov [esi + TxDesc.FlagLen], 0
mov [esi + TxDesc.PacketBuffer], 0
add esi, sizeof.TxDesc
dec ecx
jnz .tx_loop
mov [ebx + device.cur_rx], 0
mov ecx, RX_RING
lea esi, [ebx + device.rx_ring]
.rx_loop:
push ecx esi
invoke KernelAlloc, 4096 shl RBLEN ; push/pop esi not needed, but just in case...
pop esi
mov [esi + RX_RING*sizeof.RxDesc], eax
invoke GetPhysAddr
mov [esi + RxDesc.PacketBuffer], eax
mov [esi + RxDesc.FlagLen], (4096 shl RBLEN or NV_RX_AVAIL)
add esi, sizeof.RxDesc
pop ecx
dec ecx
jnz .rx_loop
pop ecx esi eax
ret
; Input: none
; Output: none
align 4
txrx_reset:
push eax esi
DEBUGF 1,"txrx_reset\n"
mov edi, [ebx + device.mmio_addr]
mov eax, [ebx + device.desc_ver]
or eax, (TXRXCTL_BIT2 or TXRXCTL_RESET)
mov [TxRxControl], eax
call pci_push
mov esi, NV_TXRX_RESET_DELAY
invoke Sleep
mov eax, [ebx + device.desc_ver]
or eax, TXRXCTL_BIT2
mov [TxRxControl], eax
call pci_push
pop esi eax
ret
; Input: none
; Output: none
set_multicast:
; u32 addr[2];
; u32 mask[2];
; u32 pff;
; u32 alwaysOff[2];
; u32 alwaysOn[2];
;
; memset(addr, 0, sizeof(addr));
; memset(mask, 0, sizeof(mask));
;
; pff = PFF_MYADDR;
;
; alwaysOn[0] = alwaysOn[1] = alwaysOff[0] = alwaysOff[1] = 0;
;
; addr[0] = alwaysOn[0];
; addr[1] = alwaysOn[1];
; mask[0] = alwaysOn[0] | alwaysOff[0];
; mask[1] = alwaysOn[1] | alwaysOff[1];
;
; addr[0] |= MCASTADDRA_FORCE;
; pff |= PFF_ALWAYS;
call stop_rx
mov edi, [ebx + device.mmio_addr]
mov [MulticastAddrA], MCASTADDRA_FORCE
mov [MulticastAddrB], 0
mov [MulticastMaskA], 0
mov [MulticastMaskB], 0
mov [PacketFilterFlags], (PFF_MYADDR or PFF_ALWAYS)
call start_rx
ret
; Input: none
; Output: none
start_rx:
push edi
DEBUGF 1,"start_rx\n"
; Already running? Stop it.
mov edi, [ebx + device.mmio_addr]
mov eax, [ReceiverControl]
test eax, RCVCTL_START
jz @f
mov [ReceiverControl], 0
call pci_push
@@:
mov eax, [ebx + device.linkspeed]
mov [LinkSpeed], eax
call pci_push
mov [ReceiverControl], RCVCTL_START
call pci_push
pop edi
ret
; Input: none
; Output: none
stop_rx:
push esi edi
DEBUGF 1,"stop_rx.\n"
mov edi, [ebx + device.mmio_addr]
mov [ReceiverControl], 0
push ebx edx edi
stdcall reg_delay, ReceiverStatus-edi, RCVSTAT_BUSY, 0, NV_RXSTOP_DELAY1, NV_RXSTOP_DELAY1MAX, 0
pop edi edx ebx
mov esi, NV_RXSTOP_DELAY2
invoke Sleep
mov [LinkSpeed], 0
pop edi esi
ret
; Input: none
; Output: EAX
update_linkspeed:
DEBUGF 1,"update linkspeed\n"
; BMSR_LSTATUS is latched, read it twice: we want the current value.
mov edx, [ebx + device.phyaddr]
mov eax, MII_BMSR
mov ecx, MII_READ
call mii_rw
mov eax, MII_BMSR
mov ecx, MII_READ
call mii_rw
test ax, BMSR_LSTATUS ; Link up?
jz .no_link
DEBUGF 1,"link is up\n"
test ax, BMSR_ANEGCOMPLETE ; still in autonegotiation?
jz .10mbit_hd
DEBUGF 1,"autonegotiation is complete\n"
cmp [ebx + device.gigabit], PHY_GIGABIT
jne .no_gigabit
;mov edx, [ebx + device.phyaddr]
mov eax, MII_CTRL1000
mov ecx, MII_READ
call mii_rw
push eax
;mov edx, [ebx + device.phyaddr]
mov eax, MII_STAT1000
mov ecx, MII_READ
call mii_rw
pop ecx
test eax, LPA_1000FULL
jz .no_gigabit
test ecx, ADVERTISE_1000FULL
jz .no_gigabit
DEBUGF 1,"update_linkspeed: GBit ethernet detected.\n"
mov [ebx + device.state], ETH_LINK_1G
mov ecx, (LINKSPEED_FORCE or LINKSPEED_1000)
xor eax, eax
inc eax
jmp set_speed
.no_gigabit:
;mov edx, [ebx + device.phyaddr]
mov eax, MII_ADVERTISE
mov ecx, MII_READ
call mii_rw ; adv = eax
push eax
;mov edx, [ebx + device.phyaddr]
mov eax, MII_LPA
mov ecx, MII_READ
call mii_rw ; lpa = eax
pop ecx
DEBUGF 1,"PHY advertises 0x%x, lpa 0x%x\n", cx, ax
and eax, ecx ; FIXME: handle parallel detection properly, handle gigabit ethernet
test eax, LPA_100FULL
jz @f
DEBUGF 1,"update_linkspeed: 100 mbit full duplex\n"
mov [ebx + device.state], ETH_LINK_100M + ETH_LINK_FD
mov ecx, (LINKSPEED_FORCE or LINKSPEED_100)
xor eax, eax
inc eax
jmp set_speed
@@:
test eax, LPA_100HALF
jz @f
DEBUGF 1,"update_linkspeed: 100 mbit half duplex\n"
mov [ebx + device.state], ETH_LINK_100M
mov ecx, (LINKSPEED_FORCE or LINKSPEED_100)
xor eax, eax
jmp set_speed
@@:
test eax, LPA_10FULL
jz @f
DEBUGF 1,"update_linkspeed: 10 mbit full duplex\n"
mov [ebx + device.state], ETH_LINK_10M + ETH_LINK_FD
mov ecx, (LINKSPEED_FORCE or LINKSPEED_10)
xor eax, eax
inc eax
jmp set_speed
@@:
.10mbit_hd:
DEBUGF 1,"update_linkspeed: 10 mbit half duplex\n"
mov [ebx + device.state], ETH_LINK_10M
mov ecx, (LINKSPEED_FORCE or LINKSPEED_10)
xor eax, eax
jmp set_speed
.no_link:
DEBUGF 1,"update_linkspeed: link is down\n"
mov [ebx + device.state], ETH_LINK_DOWN
mov ecx, (LINKSPEED_FORCE or LINKSPEED_10)
xor eax, eax
jmp set_speed
align 4
set_speed:
cmp eax, [ebx + device.duplex]
jne .update
cmp ecx, [ebx + device.linkspeed]
jne .update
ret
.update:
DEBUGF 1,"update_linkspeed: changing link to 0x%x/XD.\n", ecx
mov [ebx + device.duplex], eax
mov [ebx + device.linkspeed], ecx
cmp [ebx + device.gigabit], PHY_GIGABIT
jne .no_gigabit
mov edi, [ebx + device.mmio_addr]
mov eax, [RandomSeed]
and eax, not (0x3FF00)
mov ecx, eax ; phyreg = ecx
mov eax, [ebx + device.linkspeed]
and eax, 0xFFF
cmp eax, LINKSPEED_10
jne @f
or ecx, RNDSEED_FORCE3
jmp .end_if4
@@:
cmp eax, LINKSPEED_100
jne @f
or ecx, RNDSEED_FORCE2
jmp .end_if4
@@:
cmp eax, LINKSPEED_1000
jne .end_if4
or ecx, RNDSEED_FORCE
.end_if4:
mov [RandomSeed], ecx
.no_gigabit:
mov ecx, [PhyInterface]
and ecx, not (PHY_HALF or PHY_100 or PHY_1000)
cmp [ebx + device.duplex], 0
jne @f
or ecx, PHY_HALF
@@:
mov eax, [ebx + device.linkspeed]
and eax, 0xFFF
cmp eax, LINKSPEED_100
jne @f
or ecx, PHY_100
jmp .end_if5
@@:
cmp eax, LINKSPEED_1000
jne .end_if5
or ecx, PHY_1000
.end_if5:
mov [PhyInterface], ecx
cmp [ebx + device.duplex], 0
je @f
xor ecx, ecx
jmp .next
@@:
mov ecx, MISC1_HD
.next:
or ecx, MISC1_FORCE
mov [Misc1], ecx
call pci_push
mov eax, [ebx + device.linkspeed]
mov [LinkSpeed], eax
call pci_push
ret
align 4
read_mac:
mov edi, [ebx + device.mmio_addr]
mov eax, [MacAddrA]
mov ecx, [MacAddrB]
mov dword [ebx + device.mac], eax
mov word [ebx + device.mac + 4], cx
cmp [ebx + device.device_id], 0x03E5
jae @f
bswap eax
xchg cl, ch
mov dword [ebx + device.mac + 2], eax
mov word [ebx + device.mac], cx
@@:
DEBUGF 1,"MAC = %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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Transmit ;;
;; ;;
;; In: buffer pointer in [esp+4] ;;
;; size of buffer in [esp+8] ;;
;; pointer to device structure in ebx ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc transmit stdcall bufferptr, buffersize
pushf
cli
DEBUGF 1,"Transmitting packet, buffer:%x, size:%u\n", [bufferptr], [buffersize]
mov eax, [bufferptr]
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 [buffersize], 1514
ja .fail
cmp [buffersize], 60
jb .fail
; get the descriptor address
mov eax, [ebx + device.cur_tx]
shl eax, 3 ; TX descriptor is 8 bytes.
lea esi, [ebx + device.tx_ring + eax]
mov eax, [bufferptr]
mov [esi + TX_RING*sizeof.TxDesc], eax
invoke GetPhysAddr ; Does not change esi/ebx :)
mov [esi + TxDesc.PacketBuffer], eax
mov eax, [buffersize]
or eax, [ebx + device.txflags]
mov [esi + TxDesc.FlagLen], eax
mov edi, [ebx + device.mmio_addr]
mov eax, [ebx + device.desc_ver]
or eax, TXRXCTL_KICK
mov [TxRxControl], eax
call pci_push
inc [ebx + device.cur_tx]
and [ebx + device.cur_tx], (TX_RING-1)
; Update stats
inc [ebx + device.packets_tx]
mov eax, [buffersize]
add dword[ebx + device.bytes_tx], eax
adc dword[ebx + device.bytes_tx + 4], 0
popf
xor eax, eax
ret
.fail:
DEBUGF 2,"Send failed\n"
invoke KernelFree, [bufferptr]
popf
or eax, -1
ret
endp
; Interrupt handler
align 4
int_handler:
push ebx esi edi
DEBUGF 1,"INT\n"
;-------------------------------------------
; Find pointer of device wich made IRQ occur
mov esi, device_list
mov ecx, [devices]
test ecx, ecx
jz .fail
.nextdevice:
mov ebx, dword [esi]
add esi, 4
mov edi, [ebx + device.mmio_addr]
mov eax, [IrqStatus]
test eax, eax
jnz .got_it
dec ecx
jnz .nextdevice
.nothing:
pop edi esi ebx
xor eax, eax
ret
.got_it:
mov [IrqStatus], eax
DEBUGF 1,"IrqStatus = %x\n", eax
test eax, IRQ_RX ;+ IRQ_TIMER ;;;;
jz .no_rx
push ebx
.more_rx:
pop ebx
mov eax, [ebx + device.cur_rx]
mov cx, sizeof.RxDesc
mul cx
lea esi, [ebx + device.rx_ring + eax]
mov eax, [esi + RxDesc.FlagLen]
test eax, NV_RX_AVAIL ; still owned by hardware
jnz .no_rx
cmp [ebx + device.desc_ver], DESC_VER_1
jne @f
test eax, NV_RX_DESCRIPTORVALID
jz .no_rx
jmp .next
@@:
test eax, NV_RX2_DESCRIPTORVALID
jz .no_rx
.next:
cmp dword[ebx + device.desc_ver], DESC_VER_1
jne @f
and eax, LEN_MASK_V1
jmp .next2
@@:
and eax, LEN_MASK_V2
.next2:
DEBUGF 1,"Received %u bytes\n", eax
; Update stats
add dword[ebx + device.bytes_rx], eax
adc dword[ebx + device.bytes_rx + 4], 0
inc dword[ebx + device.packets_rx]
; Prepare to give packet to kernel
push ebx
push .more_rx
push eax
push dword[esi + RX_RING*sizeof.RxDesc]
DEBUGF 1,"packet ptr=0x%x\n", [esi + RX_RING*sizeof.RxDesc]
; Allocate new buffer for this descriptor
invoke KernelAlloc, 4096 shl RBLEN
mov [esi + RX_RING*sizeof.RxDesc], eax
invoke GetPhysAddr
mov [esi + RxDesc.PacketBuffer], eax
mov [esi + RxDesc.FlagLen], (4096 shl RBLEN or NV_RX_AVAIL)
; update current RX descriptor
inc [ebx + device.cur_rx]
and [ebx + device.cur_rx], (RX_RING-1)
jmp [Eth_input]
.no_rx:
test eax, IRQ_RX_ERROR
jz .no_rx_err
push eax
DEBUGF 2,"RX error!\n"
mov eax, [ebx + device.cur_rx]
mov cx, sizeof.RxDesc
mul cx
lea esi, [ebx + device.rx_ring + eax]
mov eax, [esi + RxDesc.FlagLen]
DEBUGF 1,"Flaglen=%x\n", eax
; TODO: allocate new buff ?
pop eax
.no_rx_err:
test eax, IRQ_TX_ERROR
jz .no_tx_err
DEBUGF 2,"TX error!\n"
; TODO
.no_tx_err:
test eax, IRQ_LINK
jz .no_link
push eax
call update_linkspeed
pop eax
.no_link:
test eax, IRQ_TX_OK
jz .no_tx
DEBUGF 1, "TX completed\n"
.loop_tx:
mov esi, [ebx + device.last_tx]
shl esi, 3 ; TX descriptor is 8 bytes.
lea esi, [ebx + device.tx_ring + esi]
DEBUGF 1,"Flaglen = 0x%x\n", [esi + TxDesc.FlagLen]
test [esi + TxDesc.FlagLen], NV_TX_VALID
jnz .no_tx
cmp dword[esi + TX_RING*sizeof.TxDesc], 0
je .no_tx
DEBUGF 1,"Freeing buffer 0x%x\n", [esi + TX_RING*sizeof.TxDesc]:8
push dword[esi + TX_RING*sizeof.TxDesc]
mov dword[esi + TX_RING*sizeof.TxDesc], 0
invoke KernelFree
inc [ebx + device.last_tx]
and [ebx + device.last_tx], TX_RING - 1
jmp .loop_tx
.no_tx:
.fail:
pop edi esi ebx
xor eax, eax
inc eax
ret
; End of code
data fixups
end data
include '../peimport.inc'
my_service db 'FORCEDETH',0 ; max 16 chars include zero
include_debug_strings
align 4
devices dd 0
device_list rd MAX_DEVICES ; This list contains all pointers to device structures the driver is handling