kolibrios/drivers/ethernet/R6040.asm

1121 lines
32 KiB
NASM
Raw Normal View History

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2021. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; R6040 driver for KolibriOS ;;
;; ;;
;; based on R6040.c from linux ;;
;; ;;
;; Written by Asper (asper.85@mail.ru) ;;
;; and hidnplayr (hidnplayr@gmail.com) ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; TODO: test for RX-overrun
format PE DLL native
entry START
CURRENT_API = 0x0200
COMPATIBLE_API = 0x0100
API_VERSION = (COMPATIBLE_API shl 16) + CURRENT_API
MAX_DEVICES = 16
__DEBUG__ = 1
__DEBUG_LEVEL__ = 2
W_MAX_TIMEOUT = 0x0FFF ; max time out delay time
TX_TIMEOUT = 6000 ; Time before concluding the transmitter is hung, in ms
TX_RING_SIZE = 16 ; RING sizes must be a power of 2
RX_RING_SIZE = 16
RX_BUF_LEN_IDX = 3 ; 0==8K, 1==16K, 2==32K, 3==64K
; Threshold is bytes transferred to chip before transmission starts.
TX_FIFO_THRESH = 256 ; In bytes, rounded down to 32 byte units.
; The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024.
RX_FIFO_THRESH = 4 ; Rx buffer level before first PCI xfer.
RX_DMA_BURST = 4 ; Maximum PCI burst, '4' is 256 bytes
TX_DMA_BURST = 4
section '.flat' readable writable executable
include '../proc32.inc'
include '../struct.inc'
include '../macros.inc'
include '../fdo.inc'
include '../netdrv.inc'
; Operational parameters that usually are not changed.
PHY1_ADDR = 1 ; For MAC1
PHY2_ADDR = 3 ; For MAC2
PHY_MODE = 0x3100 ; PHY CHIP Register 0
PHY_CAP = 0x01E1 ; PHY CHIP Register 4
;**************************************************************************
; RDC R6040 Register Definitions
;**************************************************************************
MCR0 = 0x00 ; Control register 0
MCR0_RCVEN = 0x0002 ; Receive enable
MCR0_PROMISC = 0x0020 ; Promiscuous mode
MCR0_HASH_EN = 0x0100 ; Enable multicast hash table function
MCR0_XMTEN = 0x1000 ; Transmission enable
MCR0_FD = 0x8000 ; Full/Half Duplex mode
MCR1 = 0x01 ; Control register 1
MAC_RST = 0x0001 ; Reset the MAC
MBCR = 0x08 ; Bus control
MT_ICR = 0x0C ; TX interrupt control
MR_ICR = 0x10 ; RX interrupt control
MTPR = 0x14 ; TX poll command register
MR_BSR = 0x18 ; RX buffer size
MR_DCR = 0x1A ; RX descriptor control
MLSR = 0x1C ; Last status
MMDIO = 0x20 ; MDIO control register
MDIO_WRITE = 0x4000 ; MDIO write
MDIO_READ = 0x2000 ; MDIO read
MMRD = 0x24 ; MDIO read data register
MMWD = 0x28 ; MDIO write data register
MTD_SA0 = 0x2C ; TX descriptor start address 0
MTD_SA1 = 0x30 ; TX descriptor start address 1
MRD_SA0 = 0x34 ; RX descriptor start address 0
MRD_SA1 = 0x38 ; RX descriptor start address 1
MISR = 0x3C ; Status register
MIER = 0x40 ; INT enable register
MSK_INT = 0x0000 ; Mask off interrupts
RX_FINISH = 0x0001 ; RX finished
RX_NO_DESC = 0x0002 ; No RX descriptor available
RX_FIFO_FULL = 0x0004 ; RX FIFO full
RX_EARLY = 0x0008 ; RX early
TX_FINISH = 0x0010 ; TX finished
TX_EARLY = 0x0080 ; TX early
EVENT_OVRFL = 0x0100 ; Event counter overflow
LINK_CHANGED = 0x0200 ; PHY link changed
ME_CISR = 0x44 ; Event counter INT status
ME_CIER = 0x48 ; Event counter INT enable
MR_CNT = 0x50 ; Successfully received packet counter
ME_CNT0 = 0x52 ; Event counter 0
ME_CNT1 = 0x54 ; Event counter 1
ME_CNT2 = 0x56 ; Event counter 2
ME_CNT3 = 0x58 ; Event counter 3
MT_CNT = 0x5A ; Successfully transmit packet counter
ME_CNT4 = 0x5C ; Event counter 4
MP_CNT = 0x5E ; Pause frame counter register
MAR0 = 0x60 ; Hash table 0
MAR1 = 0x62 ; Hash table 1
MAR2 = 0x64 ; Hash table 2
MAR3 = 0x66 ; Hash table 3
MID_0L = 0x68 ; Multicast address MID0 Low
MID_0M = 0x6A ; Multicast address MID0 Medium
MID_0H = 0x6C ; Multicast address MID0 High
MID_1L = 0x70 ; MID1 Low
MID_1M = 0x72 ; MID1 Medium
MID_1H = 0x74 ; MID1 High
MID_2L = 0x78 ; MID2 Low
MID_2M = 0x7A ; MID2 Medium
MID_2H = 0x7C ; MID2 High
MID_3L = 0x80 ; MID3 Low
MID_3M = 0x82 ; MID3 Medium
MID_3H = 0x84 ; MID3 High
PHY_CC = 0x88 ; PHY status change configuration register
PHY_ST = 0x8A ; PHY status register
MAC_SM = 0xAC ; MAC status machine
MAC_ID = 0xBE ; Identifier register
MAX_BUF_SIZE = 1514
MBCR_DEFAULT = 0x012A ; MAC Bus Control Register
MCAST_MAX = 3 ; Max number multicast addresses to filter
;Descriptor status
DSC_OWNER_MAC = 0x8000 ; MAC is the owner of this descriptor
DSC_RX_OK = 0x4000 ; RX was successfull
DSC_RX_ERR = 0x0800 ; RX PHY error
DSC_RX_ERR_DRI = 0x0400 ; RX dribble packet
DSC_RX_ERR_BUF = 0x0200 ; RX length exceeds buffer size
DSC_RX_ERR_LONG = 0x0100 ; RX length > maximum packet length
DSC_RX_ERR_RUNT = 0x0080 ; RX packet length < 64 byte
DSC_RX_ERR_CRC = 0x0040 ; RX CRC error
DSC_RX_BCAST = 0x0020 ; RX broadcast (no error)
DSC_RX_MCAST = 0x0010 ; RX multicast (no error)
DSC_RX_MCH_HIT = 0x0008 ; RX multicast hit in hash table (no error)
DSC_RX_MIDH_HIT = 0x0004 ; RX MID table hit (no error)
DSC_RX_IDX_MID_MASK = 3 ; RX mask for the index of matched MIDx
;PHY settings
ICPLUS_PHY_ID = 0x0243
RX_INTS = RX_FIFO_FULL or RX_NO_DESC or RX_FINISH
TX_INTS = TX_FINISH
INT_MASK = RX_INTS or TX_INTS
RX_BUF_LEN equ (8192 << RX_BUF_LEN_IDX) ; Size of the in-memory receive ring.
IO_SIZE = 256 ; RDC MAC I/O Size
MAX_MAC = 2 ; MAX RDC MAC
struct x_head
status dw ? ;0-1
len dw ? ;2-3
buf dd ? ;4-7
ndesc dd ? ;8-B
rev1 dd ? ;C-F
vbufp dd ? ;10-13
vndescp dd ? ;14-17
skb_ptr dd ? ;18-1B
rev2 dd ? ;1C-1F
ends
struct device ETH_DEVICE
io_addr dd ?
pci_bus dd ?
pci_dev dd ?
irq_line db ?
rb 3 ; align 4
cur_rx dw ?
cur_tx dw ?
last_tx dw ?
phy_addr dd ?
phy_mode dw ?
mcr0 dw ?
mcr1 dw ?
switch_sig dw ?
rb 0x100 - ($ and 0xff) ; align 256
tx_ring rb ((TX_RING_SIZE*sizeof.x_head+32) and 0xfffffff0)
rx_ring rb ((RX_RING_SIZE*sizeof.x_head+32) and 0xfffffff0)
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) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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]
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], unload
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]:8
; Ok, the eth_device structure is ready, let's probe the device
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] ;
call probe ; this function will output in eax
test eax, eax
jnz .err2
DEBUGF 2,"Initialised OK\n"
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 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
.err2:
dec [devices]
.err:
invoke KernelFree, ebx
.fail:
DEBUGF 2, "Failed to load\n"
or eax, -1
ret
;------------------------------------------------------
endp
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
;; ;;
;; Actual Hardware dependent code starts here ;;
;; ;;
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
align 4
unload:
; TODO: (in this particular order)
;
; - Stop the device
; - Detach int handler
; - Remove device from local list (device_list)
; - call unregister function in kernel
; - Remove all allocated structures and buffers the card used
or eax, -1
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; probe: enables the device (if it really is R6040)
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
align 4
probe:
DEBUGF 1,"Probing\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
; If PHY status change register is still set to zero
; it means the bootloader didn't initialize it
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], PHY_CC
in ax, dx
test ax, ax
jnz @f
mov ax, 0x9F07
out dx, ax
@@:
call read_mac
; Some bootloaders/BIOSes do not initialize MAC address, warn about that
and eax, 0xFF
or eax, dword [ebx + device.mac]
test eax, eax
jnz @f
DEBUGF 2, "MAC address not initialized!\n"
@@:
; Init RDC private data
mov [ebx + device.mcr0], MCR0_XMTEN or MCR0_RCVEN
mov [ebx + device.phy_addr], PHY1_ADDR
mov [ebx + device.switch_sig], 0
; Check the vendor ID on the PHY, if 0xFFFF assume none attached
stdcall phy_read, [ebx + device.phy_addr], 2
cmp ax, 0xFFFF
jne @f
DEBUGF 2, "Failed to detect an attached PHY!\n"
.err:
mov eax, -1
ret
@@:
; Set MAC address
call init_mac_regs
; Initialize and alloc RX/TX buffers
call init_txbufs
call init_rxbufs
test eax, eax
jnz .err
; Read the PHY ID
mov [ebx + device.phy_mode], MCR0_FD
stdcall phy_read, 0, 2
mov [ebx + device.switch_sig], ax
cmp ax, ICPLUS_PHY_ID
jne @f
stdcall phy_write, 29, 31, 0x175C ; Enable registers
jmp .phy_readen
@@:
; PHY Mode Check
stdcall phy_write, [ebx + device.phy_addr], 4, PHY_CAP
stdcall phy_write, [ebx + device.phy_addr], 0, PHY_MODE
if PHY_MODE = 0x3100
call phy_mode_chk
mov [ebx + device.phy_mode], ax
jmp .phy_readen
end if
if not (PHY_MODE and 0x0100)
mov [ebx + device.phy_mode], 0
end if
.phy_readen:
; Set duplex mode
mov ax, [ebx + device.phy_mode]
or [ebx + device.mcr0], ax
; improve performance (by RDC guys)
stdcall phy_read, 30, 17
or ax, 0x4000
stdcall phy_write, 30, 17, eax
stdcall phy_read, 30, 17
and ax, not 0x2000
stdcall phy_write, 30, 17, eax
stdcall phy_write, 0, 19, 0x0000
stdcall phy_write, 0, 30, 0x01F0
; Initialize all Mac registers
call init_mac_regs
align 4
reset:
DEBUGF 1,"Resetting\n"
; Mask off Interrupt
xor ax, ax
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], MIER
out dx, ax
; attach int handler
movzx eax, [ebx + device.irq_line]
DEBUGF 1,"Attaching int handler to irq %x\n", eax:1
invoke AttachIntHandler, eax, int_handler, ebx
test eax, eax
jnz @f
DEBUGF 2,"Could not attach int handler!\n"
or eax, -1
ret
@@:
;Reset RDC MAC
mov eax, MAC_RST
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], MCR1
out dx, ax
mov ecx, 2048 ;limit
.read:
in ax, dx
test ax, 0x1
jnz @f
dec ecx
test ecx, ecx
jnz .read
@@:
;Reset internal state machine
mov ax, 2
set_io [ebx + device.io_addr], MAC_SM
out dx, ax
xor ax, ax
out dx, ax
mov esi, 5
invoke Sleep
;MAC Bus Control Register
mov ax, MBCR_DEFAULT
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], MBCR
out dx, ax
;Buffer Size Register
mov ax, MAX_BUF_SIZE
set_io [ebx + device.io_addr], MR_BSR
out dx, ax
;Write TX ring start address
lea eax, [ebx + device.tx_ring]
invoke GetPhysAddr
set_io [ebx + device.io_addr], MTD_SA0
out dx, ax
shr eax, 16
set_io [ebx + device.io_addr], MTD_SA1
out dx, ax
;Write RX ring start address
lea eax, [ebx + device.rx_ring]
invoke GetPhysAddr
set_io [ebx + device.io_addr], MRD_SA0
out dx, ax
shr eax, 16
set_io [ebx + device.io_addr], MRD_SA1
out dx, ax
;Set interrupt waiting time and packet numbers
xor ax, ax
set_io [ebx + device.io_addr], MT_ICR
out dx, ax
;Enable interrupts
mov ax, INT_MASK
set_io [ebx + device.io_addr], MIER
out dx, ax
;Enable RX
mov ax, [ebx + device.mcr0]
or ax, MCR0_RCVEN
set_io [ebx + device.io_addr], MCR0
out dx, ax
;Let TX poll the descriptors
;we may got called by tx_timeout which has left some unset tx buffers
xor ax, ax
inc ax
set_io [ebx + device.io_addr], MTPR
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
DEBUGF 1,"Reset ok\n"
xor eax, eax
ret
align 4
init_txbufs:
DEBUGF 1,"Init TxBufs\n"
lea esi, [ebx + device.tx_ring]
lea eax, [ebx + device.tx_ring + sizeof.x_head]
invoke GetPhysAddr
mov ecx, TX_RING_SIZE
.next_desc:
mov [esi + x_head.ndesc], eax
mov [esi + x_head.skb_ptr], 0
mov [esi + x_head.status], DSC_OWNER_MAC
add eax, sizeof.x_head
add esi, sizeof.x_head
dec ecx
jnz .next_desc
lea eax, [ebx + device.tx_ring]
invoke GetPhysAddr
mov dword[ebx + device.tx_ring + sizeof.x_head*(TX_RING_SIZE-1) + x_head.ndesc], eax
ret
align 4
init_rxbufs:
DEBUGF 1,"Init RxBufs\n"
lea esi, [ebx + device.rx_ring]
lea eax, [ebx + device.rx_ring + sizeof.x_head]
invoke GetPhysAddr
mov edx, eax
mov ecx, RX_RING_SIZE
.next_desc:
mov [esi + x_head.ndesc], edx
push esi ecx edx
invoke NetAlloc, MAX_BUF_SIZE+NET_BUFF.data
pop edx ecx esi
test eax, eax
jz .out_of_mem
mov [esi + x_head.skb_ptr], eax
invoke GetPhysAddr
add eax, NET_BUFF.data
mov [esi + x_head.buf], eax
mov [esi + x_head.status], DSC_OWNER_MAC
add edx, sizeof.x_head
add esi, sizeof.x_head
dec ecx
jnz .next_desc
; complete the ring by linking the last to the first
lea eax, [ebx + device.rx_ring]
invoke GetPhysAddr
mov dword[ebx + device.rx_ring + sizeof.x_head*(RX_RING_SIZE-1) + x_head.ndesc], eax
xor eax, eax
ret
.out_of_mem:
or eax, -1
ret
align 4
phy_mode_chk:
DEBUGF 1,"Checking PHY mode\n"
; PHY Link Status Check
stdcall phy_read, [ebx + device.phy_addr], MII_BMSR
test ax, BMSR_LSTATUS
jz .ret_0x8000
; PHY Chip Auto-Negotiation Status
test ax, BMSR_ANEGCOMPLETE
jnz .auto_nego
; Force Mode
stdcall phy_read, [ebx + device.phy_addr], MII_BMCR
test ax, BMCR_FULLDPLX
jnz .ret_0x8000
.auto_nego:
; Auto Negotiation Mode
stdcall phy_read, [ebx + device.phy_addr], MII_LPA
mov cx, ax
stdcall phy_read, [ebx + device.phy_addr], MII_ADVERTISE
and ax, cx
test ax, ADVERTISE_10FULL + ADVERTISE_100FULL
jnz .ret_0x8000
xor eax, eax
ret
.ret_0x8000:
mov eax, 0x8000
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Transmit ;;
;; ;;
;; In: pointer to device structure in ebx ;;
;; Out: eax = 0 on success ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
align 16
proc 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
movzx edi, [ebx + device.cur_tx]
shl edi, 5
add edi, ebx
add edi, device.tx_ring
DEBUGF 1,"TX buffer status: 0x%x\n", [edi + x_head.status]:4
test [edi + x_head.status], DSC_OWNER_MAC ; check if buffer is available
jnz .overrun
mov [edi + x_head.skb_ptr], esi
mov eax, esi
add eax, [eax + NET_BUFF.offset]
invoke GetPhysAddr
mov [edi + x_head.buf], eax
mov ecx, [esi + NET_BUFF.length]
mov [edi + x_head.len], cx
mov [edi + x_head.status], DSC_OWNER_MAC
; Trigger the MAC to check the TX descriptor
mov ax, 0x01
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], MTPR
out dx, ax
inc [ebx + device.cur_tx]
and [ebx + device.cur_tx], TX_RING_SIZE - 1
; 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
;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Interrupt handler ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;
align 16
int_handler:
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], MISR
in ax, dx
test ax, ax
jz .nothing
out dx, ax ; ACK interrupt
DEBUGF 1,"Status: %x\n", ax
push ax
test word[esp], RX_FINISH
jz .no_RX
push ebx
.more_RX:
pop ebx
; Find the current RX descriptor
movzx edx, [ebx + device.cur_rx]
shl edx, 5
lea edx, [ebx + device.rx_ring + edx]
; Check the descriptor status
mov cx, [edx + x_head.status]
test cx, DSC_OWNER_MAC
jnz .no_RX
DEBUGF 1,"packet status=0x%x\n", cx
test cx, DSC_RX_ERR ; Global error status set
jnz .no_RX
; Packet successfully received
movzx ecx, [edx + x_head.len]
and ecx, 0xFFF
sub ecx, 4 ; Do not count the CRC
DEBUGF 1,"packet ptr=0x%x size=%u\n", [edx + x_head.skb_ptr], ecx
; Update stats
add dword[ebx + device.bytes_rx], ecx
adc dword[ebx + device.bytes_rx + 4], 0
inc dword[ebx + device.packets_rx]
push ebx
; Push packet ptr and return addr for Eth_input
push .more_RX
mov eax, [edx + x_head.skb_ptr]
push eax
mov [eax + NET_BUFF.length], ecx
mov [eax + NET_BUFF.device], ebx
mov [eax + NET_BUFF.offset], NET_BUFF.data
; reset the RX descriptor (alloc new buffer)
push edx
invoke NetAlloc, MAX_BUF_SIZE+NET_BUFF.data
pop edx
mov [edx + x_head.skb_ptr], eax
invoke GetPhysAddr
add eax, NET_BUFF.data
mov [edx + x_head.buf], eax
mov [edx + x_head.status], DSC_OWNER_MAC
; Use next descriptor next time
inc [ebx + device.cur_rx]
and [ebx + device.cur_rx], RX_RING_SIZE - 1
; At last, send packet to kernel
jmp [EthInput]
.no_RX:
test word[esp], TX_FINISH
jz .no_TX
.loop_tx:
movzx edi, [ebx + device.last_tx]
shl edi, 5
lea edi, [ebx + device.tx_ring + edi]
test [edi + x_head.status], DSC_OWNER_MAC
jnz .no_TX
cmp [edi + x_head.skb_ptr], 0
je .no_TX
DEBUGF 1,"Freeing buffer 0x%x\n", [edi + x_head.skb_ptr]
push [edi + x_head.skb_ptr]
mov [edi + x_head.skb_ptr], 0
invoke NetFree
inc [ebx + device.last_tx]
and [ebx + device.last_tx], TX_RING_SIZE - 1
jmp .loop_tx
.no_TX:
test word[esp], RX_NO_DESC
jz .no_rxdesc
DEBUGF 2, "No more RX descriptors!\n"
.no_rxdesc:
test word[esp], RX_FIFO_FULL
jz .no_rxfifo
DEBUGF 2, "RX FIFO full!\n"
.no_rxfifo:
test word[esp], RX_EARLY
jz .no_rxearly
DEBUGF 2, "RX early\n"
.no_rxearly:
test word[esp], TX_EARLY
jz .no_txearly
DEBUGF 2, "TX early\n"
.no_txearly:
test word[esp], EVENT_OVRFL
jz .no_ovrfl
DEBUGF 2, "Event counter overflow!\n"
.no_ovrfl:
test word[esp], LINK_CHANGED
jz .no_link
DEBUGF 2, "Link changed\n"
.no_link:
pop ax
pop edi esi ebx
xor eax, eax
inc eax
ret
.nothing:
pop edi esi ebx
xor eax, eax
ret
align 4
init_mac_regs:
DEBUGF 1,"initializing MAC regs\n"
; MAC operation register
mov ax, 1
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], MCR1
out dx, ax
; Reset MAC
mov ax, 2
set_io [ebx + device.io_addr], MAC_SM
out dx, ax
; Reset internal state machine
xor ax, ax
out dx, ax
mov esi, 5
invoke Sleep
call read_mac
ret
; Read a word data from PHY Chip
align 4
proc phy_read stdcall, phy_addr:dword, reg:dword
DEBUGF 1,"PHY read, addr=0x%x reg=0x%x\n", [phy_addr]:8, [reg]:8
mov eax, [phy_addr]
shl eax, 8
add eax, [reg]
add eax, MDIO_READ
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], MMDIO
out dx, ax
;Wait for the read bit to be cleared.
mov ecx, 2048 ;limit
.read:
in ax, dx
test ax, MDIO_READ
jz @f
dec ecx
jnz .read
@@:
set_io [ebx + device.io_addr], MMRD
in ax, dx
and eax, 0xFFFF
DEBUGF 1,"PHY read, val=0x%x\n", eax:4
ret
endp
; Write a word data to PHY Chip
align 4
proc phy_write stdcall, phy_addr:dword, reg:dword, val:dword
DEBUGF 1,"PHY write, addr=0x%x reg=0x%x val=0x%x\n", \
[phy_addr]:8, [reg]:8, [val]:8
mov eax, [val]
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], MMWD
out dx, ax
;Write the command to the MDIO bus
mov eax, [phy_addr]
shl eax, 8
add eax, [reg]
add eax, MDIO_WRITE
set_io [ebx + device.io_addr], MMDIO
out dx, ax
;Wait for the write bit to be cleared.
mov ecx, 2048 ;limit
.write:
in ax, dx
test ax, MDIO_WRITE
jz @f
dec ecx
jnz .write
@@:
DEBUGF 1,"PHY write ok\n"
ret
endp
align 4
read_mac:
DEBUGF 1,"Reading MAC:\n"
mov cx, 3
lea edi, [ebx + device.mac]
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], MID_0L
.loop:
in ax, dx
stosw
inc dx
inc dx
dec cx
jnz .loop
DEBUGF 1,"%x-%x-%x-%x-%x-%x\n",\
[edi-6]:2, [edi-5]:2, [edi-4]:2, [edi-3]:2, [edi-2]:2, [edi-1]:2
ret
; End of code
data fixups
end data
include '../peimport.inc'
my_service db 'R6040',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