mirror of
https://github.com/KolibriOS/kolibrios.git
synced 2024-12-28 01:09:41 +03:00
7c3c605db6
git-svn-id: svn://kolibrios.org@4642 a494cfbc-eb01-0410-851d-a64ba20cac60
1688 lines
48 KiB
NASM
1688 lines
48 KiB
NASM
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; ;;
|
|
;; Copyright (C) KolibriOS team 2010-2014. All rights reserved. ;;
|
|
;; Distributed under terms of the GNU General Public License ;;
|
|
;; ;;
|
|
;; rhine.asm ;;
|
|
;; ;;
|
|
;; Ethernet driver for Kolibri OS ;;
|
|
;; ;;
|
|
;; This driver is based on the via-rhine driver from ;;
|
|
;; the etherboot 5.0.6 project. The copyright statement is ;;
|
|
;; ;;
|
|
;; GNU GENERAL PUBLIC LICENSE ;;
|
|
;; Version 2, June 1991 ;;
|
|
;; ;;
|
|
;; Rewritten in flat assembler by Asper (asper.85@mail.ru) ;;
|
|
;; and hidnplayr (hidnplayr@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
|
|
|
|
__DEBUG__ = 1
|
|
__DEBUG_LEVEL__ = 2 ; 1 = all, 2 = errors only
|
|
|
|
TX_RING_SIZE = 4
|
|
RX_RING_SIZE = 4
|
|
|
|
; max time out delay time
|
|
W_MAX_TIMEOUT = 0x0FFF
|
|
|
|
; Size of the in-memory receive ring.
|
|
RX_BUF_LEN_IDX = 3 ; 0==8K, 1==16K, 2==32K, 3==64K
|
|
|
|
; PCI Tuning Parameters
|
|
; 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 transfer.
|
|
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_pe.inc'
|
|
|
|
|
|
;**************************************************************************
|
|
; VIA Rhine Register Definitions
|
|
;**************************************************************************
|
|
byPAR0 = 0x00
|
|
byRCR = 0x06
|
|
byTCR = 0x07
|
|
byCR0 = 0x08
|
|
byCR1 = 0x09
|
|
byISR0 = 0x0c
|
|
byISR1 = 0x0d
|
|
byIMR0 = 0x0e
|
|
byIMR1 = 0x0f
|
|
byMAR0 = 0x10
|
|
byMAR1 = 0x11
|
|
byMAR2 = 0x12
|
|
byMAR3 = 0x13
|
|
byMAR4 = 0x14
|
|
byMAR5 = 0x15
|
|
byMAR6 = 0x16
|
|
byMAR7 = 0x17
|
|
dwCurrentRxDescAddr = 0x18
|
|
dwCurrentTxDescAddr = 0x1c
|
|
dwCurrentRDSE0 = 0x20
|
|
dwCurrentRDSE1 = 0x24
|
|
dwCurrentRDSE2 = 0x28
|
|
dwCurrentRDSE3 = 0x2c
|
|
dwNextRDSE0 = 0x30
|
|
dwNextRDSE1 = 0x34
|
|
dwNextRDSE2 = 0x38
|
|
dwNextRDSE3 = 0x3c
|
|
dwCurrentTDSE0 = 0x40
|
|
dwCurrentTDSE1 = 0x44
|
|
dwCurrentTDSE2 = 0x48
|
|
dwCurrentTDSE3 = 0x4c
|
|
dwNextTDSE0 = 0x50
|
|
dwNextTDSE1 = 0x54
|
|
dwNextTDSE2 = 0x58
|
|
dwNextTDSE3 = 0x5c
|
|
dwCurrRxDMAPtr = 0x60
|
|
dwCurrTxDMAPtr = 0x64
|
|
byMPHY = 0x6c
|
|
byMIISR = 0x6d
|
|
byBCR0 = 0x6e
|
|
byBCR1 = 0x6f
|
|
byMIICR = 0x70
|
|
byMIIAD = 0x71
|
|
wMIIDATA = 0x72
|
|
byEECSR = 0x74
|
|
byTEST = 0x75
|
|
byGPIO = 0x76
|
|
byCFGA = 0x78
|
|
byCFGB = 0x79
|
|
byCFGC = 0x7a
|
|
byCFGD = 0x7b
|
|
wTallyCntMPA = 0x7c
|
|
wTallyCntCRC = 0x7d
|
|
bySTICKHW = 0x83
|
|
byWOLcrClr = 0xA4
|
|
byWOLcgClr = 0xA7
|
|
byPwrcsrClr = 0xAC
|
|
|
|
;--------------------- Exioaddr Definitions -------------------------
|
|
|
|
; Bits in the RCR register
|
|
RCR_RRFT2 = 0x80
|
|
RCR_RRFT1 = 0x40
|
|
RCR_RRFT0 = 0x20
|
|
RCR_PROM = 0x10
|
|
RCR_AB = 0x08
|
|
RCR_AM = 0x04
|
|
RCR_AR = 0x02
|
|
RCR_SEP = 0x01
|
|
; Bits in the TCR register
|
|
TCR_RTSF = 0x80
|
|
TCR_RTFT1 = 0x40
|
|
TCR_RTFT0 = 0x20
|
|
TCR_OFSET = 0x08
|
|
TCR_LB1 = 0x04 ; loopback[1]
|
|
TCR_LB0 = 0x02 ; loopback[0]
|
|
; Bits in the CR0 register
|
|
CR0_RDMD = 0x40 ; rx descriptor polling demand
|
|
CR0_TDMD = 0x20 ; tx descriptor polling demand
|
|
CR0_TXON = 0x10
|
|
CR0_RXON = 0x08
|
|
CR0_STOP = 0x04 ; stop NIC, default = 1
|
|
CR0_STRT = 0x02 ; start NIC
|
|
CR0_INIT = 0x01 ; start init process
|
|
; Bits in the CR1 register
|
|
CR1_SFRST = 0x80 ; software reset
|
|
CR1_RDMD1 = 0x40 ; RDMD1
|
|
CR1_TDMD1 = 0x20 ; TDMD1
|
|
CR1_KEYPAG = 0x10 ; turn on par/key
|
|
CR1_DPOLL = 0x08 ; disable rx/tx auto polling
|
|
CR1_FDX = 0x04 ; full duplex mode
|
|
CR1_ETEN = 0x02 ; early tx mode
|
|
CR1_EREN = 0x01 ; early rx mode
|
|
; Bits in the CR register
|
|
CR_RDMD = 0x0040 ; rx descriptor polling demand
|
|
CR_TDMD = 0x0020 ; tx descriptor polling demand
|
|
CR_TXON = 0x0010
|
|
CR_RXON = 0x0008
|
|
CR_STOP = 0x0004 ; stop NIC, default = 1
|
|
CR_STRT = 0x0002 ; start NIC
|
|
CR_INIT = 0x0001 ; start init process
|
|
CR_SFRST = 0x8000 ; software reset
|
|
CR_RDMD1 = 0x4000 ; RDMD1
|
|
CR_TDMD1 = 0x2000 ; TDMD1
|
|
CR_KEYPAG = 0x1000 ; turn on par/key
|
|
CR_DPOLL = 0x0800 ; disable rx/tx auto polling
|
|
CR_FDX = 0x0400 ; full duplex mode
|
|
CR_ETEN = 0x0200 ; early tx mode
|
|
CR_EREN = 0x0100 ; early rx mode
|
|
; Bits in the IMR0 register
|
|
IMR0_CNTM = 0x80
|
|
IMR0_BEM = 0x40
|
|
IMR0_RUM = 0x20
|
|
IMR0_TUM = 0x10
|
|
IMR0_TXEM = 0x08
|
|
IMR0_RXEM = 0x04
|
|
IMR0_PTXM = 0x02
|
|
IMR0_PRXM = 0x01
|
|
; define imrshadow
|
|
IMRShadow = 0x5AFF
|
|
; Bits in the IMR1 register
|
|
IMR1_INITM = 0x80
|
|
IMR1_SRCM = 0x40
|
|
IMR1_NBFM = 0x10
|
|
IMR1_PRAIM = 0x08
|
|
IMR1_RES0M = 0x04
|
|
IMR1_ETM = 0x02
|
|
IMR1_ERM = 0x01
|
|
; Bits in the ISR register
|
|
ISR_INITI = 0x8000
|
|
ISR_SRCI = 0x4000
|
|
ISR_ABTI = 0x2000
|
|
ISR_NORBF = 0x1000
|
|
ISR_PKTRA = 0x0800
|
|
ISR_RES0 = 0x0400
|
|
ISR_ETI = 0x0200
|
|
ISR_ERI = 0x0100
|
|
ISR_CNT = 0x0080
|
|
ISR_BE = 0x0040
|
|
ISR_RU = 0x0020
|
|
ISR_TU = 0x0010
|
|
ISR_TXE = 0x0008
|
|
ISR_RXE = 0x0004
|
|
ISR_PTX = 0x0002
|
|
ISR_PRX = 0x0001
|
|
; Bits in the ISR0 register
|
|
ISR0_CNT = 0x80
|
|
ISR0_BE = 0x40
|
|
ISR0_RU = 0x20
|
|
ISR0_TU = 0x10
|
|
ISR0_TXE = 0x08
|
|
ISR0_RXE = 0x04
|
|
ISR0_PTX = 0x02
|
|
ISR0_PRX = 0x01
|
|
; Bits in the ISR1 register
|
|
ISR1_INITI = 0x80
|
|
ISR1_SRCI = 0x40
|
|
ISR1_NORBF = 0x10
|
|
ISR1_PKTRA = 0x08
|
|
ISR1_ETI = 0x02
|
|
ISR1_ERI = 0x01
|
|
; ISR ABNORMAL CONDITION
|
|
ISR_ABNORMAL = ISR_BE+ISR_RU+ISR_TU+ISR_CNT+ISR_NORBF+ISR_PKTRA
|
|
; Bits in the MIISR register
|
|
MIISR_MIIERR = 0x08
|
|
MIISR_MRERR = 0x04
|
|
MIISR_LNKFL = 0x02
|
|
MIISR_SPEED = 0x01
|
|
; Bits in the MIICR register
|
|
MIICR_MAUTO = 0x80
|
|
MIICR_RCMD = 0x40
|
|
MIICR_WCMD = 0x20
|
|
MIICR_MDPM = 0x10
|
|
MIICR_MOUT = 0x08
|
|
MIICR_MDO = 0x04
|
|
MIICR_MDI = 0x02
|
|
MIICR_MDC = 0x01
|
|
; Bits in the EECSR register
|
|
EECSR_EEPR = 0x80 ; eeprom programmed status, 73h means programmed
|
|
EECSR_EMBP = 0x40 ; eeprom embedded programming
|
|
EECSR_AUTOLD = 0x20 ; eeprom content reload
|
|
EECSR_DPM = 0x10 ; eeprom direct programming
|
|
EECSR_CS = 0x08 ; eeprom CS pin
|
|
EECSR_SK = 0x04 ; eeprom SK pin
|
|
EECSR_DI = 0x02 ; eeprom DI pin
|
|
EECSR_DO = 0x01 ; eeprom DO pin
|
|
; Bits in the BCR0 register
|
|
BCR0_CRFT2 = 0x20
|
|
BCR0_CRFT1 = 0x10
|
|
BCR0_CRFT0 = 0x08
|
|
BCR0_DMAL2 = 0x04
|
|
BCR0_DMAL1 = 0x02
|
|
BCR0_DMAL0 = 0x01
|
|
; Bits in the BCR1 register
|
|
BCR1_CTSF = 0x20
|
|
BCR1_CTFT1 = 0x10
|
|
BCR1_CTFT0 = 0x08
|
|
BCR1_POT2 = 0x04
|
|
BCR1_POT1 = 0x02
|
|
BCR1_POT0 = 0x01
|
|
; Bits in the CFGA register
|
|
CFGA_EELOAD = 0x80 ; enable eeprom embedded and direct programming
|
|
CFGA_JUMPER = 0x40
|
|
CFGA_MTGPIO = 0x08
|
|
CFGA_T10EN = 0x02
|
|
CFGA_AUTO = 0x01
|
|
; Bits in the CFGB register
|
|
CFGB_PD = 0x80
|
|
CFGB_POLEN = 0x02
|
|
CFGB_LNKEN = 0x01
|
|
; Bits in the CFGC register
|
|
CFGC_M10TIO = 0x80
|
|
CFGC_M10POL = 0x40
|
|
CFGC_PHY1 = 0x20
|
|
CFGC_PHY0 = 0x10
|
|
CFGC_BTSEL = 0x08
|
|
CFGC_BPS2 = 0x04 ; bootrom select[2]
|
|
CFGC_BPS1 = 0x02 ; bootrom select[1]
|
|
CFGC_BPS0 = 0x01 ; bootrom select[0]
|
|
; Bits in the CFGD register
|
|
CFGD_GPIOEN = 0x80
|
|
CFGD_DIAG = 0x40
|
|
CFGD_MAGIC = 0x10
|
|
CFGD_RANDOM = 0x08
|
|
CFGD_CFDX = 0x04
|
|
CFGD_CEREN = 0x02
|
|
CFGD_CETEN = 0x01
|
|
; Bits in RSR
|
|
RSR_RERR = 0x00000001
|
|
RSR_CRC = 0x00000002
|
|
RSR_FAE = 0x00000004
|
|
RSR_FOV = 0x00000008
|
|
RSR_LONG = 0x00000010
|
|
RSR_RUNT = 0x00000020
|
|
RSR_SERR = 0x00000040
|
|
RSR_BUFF = 0x00000080
|
|
RSR_EDP = 0x00000100
|
|
RSR_STP = 0x00000200
|
|
RSR_CHN = 0x00000400
|
|
RSR_PHY = 0x00000800
|
|
RSR_BAR = 0x00001000
|
|
RSR_MAR = 0x00002000
|
|
RSR_RXOK = 0x00008000
|
|
RSR_ABNORMAL = RSR_RERR+RSR_LONG+RSR_RUNT
|
|
; Bits in TSR
|
|
TSR_NCR0 = 0x00000001
|
|
TSR_NCR1 = 0x00000002
|
|
TSR_NCR2 = 0x00000004
|
|
TSR_NCR3 = 0x00000008
|
|
TSR_COLS = 0x00000010
|
|
TSR_CDH = 0x00000080
|
|
TSR_ABT = 0x00000100
|
|
TSR_OWC = 0x00000200
|
|
TSR_CRS = 0x00000400
|
|
TSR_UDF = 0x00000800
|
|
TSR_TBUFF = 0x00001000
|
|
TSR_SERR = 0x00002000
|
|
TSR_JAB = 0x00004000
|
|
TSR_TERR = 0x00008000
|
|
TSR_ABNORMAL = TSR_TERR+TSR_OWC+TSR_ABT+TSR_JAB+TSR_CRS
|
|
TSR_OWN_BIT = 0x80000000
|
|
|
|
CB_DELAY_LOOP_WAIT = 10 ; 10ms
|
|
; enabled mask value of irq
|
|
W_IMR_MASK_VALUE = 0x1BFF ; initial value of IMR
|
|
|
|
; Ethernet address filter type
|
|
PKT_TYPE_DIRECTED = 0x0001 ; obsolete, directed address is always accepted
|
|
PKT_TYPE_MULTICAST = 0x0002
|
|
PKT_TYPE_ALL_MULTICAST = 0x0004
|
|
PKT_TYPE_BROADCAST = 0x0008
|
|
PKT_TYPE_PROMISCUOUS = 0x0020
|
|
PKT_TYPE_LONG = 0x2000
|
|
PKT_TYPE_RUNT = 0x4000
|
|
PKT_TYPE_ERROR = 0x8000 ; accept error packets, e.g. CRC error
|
|
|
|
; Loopback mode
|
|
|
|
NIC_LB_NONE = 0x00
|
|
NIC_LB_INTERNAL = 0x01
|
|
NIC_LB_PHY = 0x02 ; MII or Internal-10BaseT loopback
|
|
|
|
PKT_BUF_SZ = 1536 ; Size of each temporary Rx buffer.
|
|
|
|
PCI_REG_MODE3 = 0x53
|
|
MODE3_MIION = 0x04 ; in PCI_REG_MOD3 OF PCI space
|
|
|
|
; VIA Rhine revisions
|
|
VT86C100A = 0x00
|
|
VTunknown0 = 0x20
|
|
VT6102 = 0x40
|
|
VT8231 = 0x50 ; Integrated MAC
|
|
VT8233 = 0x60 ; Integrated MAC
|
|
VT8235 = 0x74 ; Integrated MAC
|
|
VT8237 = 0x78 ; Integrated MAC
|
|
VTunknown1 = 0x7C
|
|
VT6105 = 0x80
|
|
VT6105_B0 = 0x83
|
|
VT6105L = 0x8A
|
|
VT6107 = 0x8C
|
|
VTunknown2 = 0x8E
|
|
VT6105M = 0x90
|
|
|
|
; Rx status bits
|
|
RX_SBITS_RERR = 1 shl 0
|
|
RX_SBITS_CRC_ERROR = 1 shl 1
|
|
RX_SBITS_FAE = 1 shl 2
|
|
RX_SBITS_FOV = 1 shl 3
|
|
RX_SBITS_TOOLONG = 1 shl 4
|
|
RX_SBITS_RUNT = 1 shl 5
|
|
RX_SBITS_SERR = 1 shl 6
|
|
RX_SBITS_BUFF = 1 shl 7
|
|
RX_SBITS_EDP = 1 shl 8
|
|
RX_SBITS_STP = 1 shl 9
|
|
RX_SBITS_CHN = 1 shl 10
|
|
RX_SBITS_PHY = 1 shl 11
|
|
RX_SBITS_BAR = 1 shl 12
|
|
RX_SBITS_MAR = 1 shl 13
|
|
RX_SBITS_RESERVED_1 = 1 shl 14
|
|
RX_SBITS_RXOK = 1 shl 15
|
|
RX_SBITS_FRAME_LENGTH = 0x7FF shl 16
|
|
RX_SBITS_RESERVED_2 = 0xF shl 27
|
|
RX_SBITS_OWN_BIT = 1 shl 31
|
|
|
|
; Rx control bits
|
|
RX_CBITS_RX_BUF_SIZE = 0x7FF
|
|
RX_CBITS_EXTEND_RX_BUF_SIZE = 0xF shl 11
|
|
RX_CBITS_RESERVED_1 = 0x1FFFF shl 15
|
|
|
|
; Tx status bits
|
|
TX_SBITS_NCR0 = 1 shl 0
|
|
TX_SBITS_NCR1 = 1 shl 1
|
|
TX_SBITS_NCR2 = 1 shl 2
|
|
TX_SBITS_NCR3 = 1 shl 3
|
|
TX_SBITS_COLS = 1 shl 4
|
|
TX_SBITS_RESERVED_1 = 1 shl 5
|
|
TX_SBITS_CDH = 1 shl 7
|
|
TX_SBITS_ABT = 1 shl 8
|
|
TX_SBITS_OWC = 1 shl 9
|
|
TX_SBITS_CRS = 1 shl 10
|
|
TX_SBITS_UDF = 1 shl 11
|
|
TX_SBITS_TBUFF = 1 shl 12
|
|
TX_SBITS_SERR = 1 shl 13
|
|
TX_SBITS_JAB = 1 shl 14
|
|
TX_SBITS_TERR = 1 shl 15
|
|
TX_SBITS_RESERVED_2 = 0x7FFF shl 16
|
|
TX_SBITS_OWN_BIT = 1 shl 31
|
|
|
|
; Tx control bits
|
|
TX_CBITS_TX_BUF_SIZE = 0x7FF
|
|
TX_CBITS_EXTEND_TX_BUF_SIZE = 0xF shl 11
|
|
TX_CBITS_CHN = 1 shl 15
|
|
TX_CBITS_CRC = 1 shl 16
|
|
TX_CBITS_RESERVED_1 = 0xF shl 17
|
|
TX_CBITS_STP = 1 shl 21
|
|
TX_CBITS_EDP = 1 shl 22
|
|
TX_CBITS_IC = 1 shl 23
|
|
TX_CBITS_RESERVED_2 = 0xFF shl 24
|
|
|
|
|
|
|
|
; Offsets to the device registers.
|
|
StationAddr = 0x00
|
|
RxConfig = 0x06
|
|
TxConfig = 0x07
|
|
ChipCmd = 0x08
|
|
IntrStatus = 0x0C
|
|
IntrEnable = 0x0E
|
|
MulticastFilter0 = 0x10
|
|
MulticastFilter1 = 0x14
|
|
RxRingPtr = 0x18
|
|
TxRingPtr = 0x1C
|
|
GFIFOTest = 0x54
|
|
MIIPhyAddr = 0x6C
|
|
MIIStatus = 0x6D
|
|
PCIBusConfig = 0x6E
|
|
MIICmd = 0x70
|
|
MIIRegAddr = 0x71
|
|
MIIData = 0x72
|
|
MACRegEEcsr = 0x74
|
|
ConfigA = 0x78
|
|
ConfigB = 0x79
|
|
ConfigC = 0x7A
|
|
ConfigD = 0x7B
|
|
RxMissed = 0x7C
|
|
RxCRCErrs = 0x7E
|
|
MiscCmd = 0x81
|
|
StickyHW = 0x83
|
|
IntrStatus2 = 0x84
|
|
WOLcrClr = 0xA4
|
|
WOLcgClr = 0xA7
|
|
PwrcsrClr = 0xAC
|
|
|
|
; Bits in the interrupt status/mask registers.
|
|
IntrRxDone = 0x0001
|
|
IntrRxErr = 0x0004
|
|
IntrRxEmpty = 0x0020
|
|
IntrTxDone = 0x0002
|
|
IntrTxError = 0x0008
|
|
IntrTxUnderrun = 0x0010
|
|
IntrPCIErr = 0x0040
|
|
IntrStatsMax = 0x0080
|
|
IntrRxEarly = 0x0100
|
|
IntrRxOverflow = 0x0400
|
|
IntrRxDropped = 0x0800
|
|
IntrRxNoBuf = 0x1000
|
|
IntrTxAborted = 0x2000
|
|
IntrLinkChange = 0x4000
|
|
IntrRxWakeUp = 0x8000
|
|
IntrNormalSummary = 0x0003
|
|
IntrAbnormalSummary = 0xC260
|
|
IntrTxDescRace = 0x080000 ; mapped from IntrStatus2
|
|
IntrTxErrSummary = 0x082218
|
|
|
|
DEFAULT_INTR = (IntrRxDone or IntrRxErr or IntrRxEmpty or IntrRxOverflow or IntrRxDropped or IntrRxNoBuf)
|
|
RX_BUF_LEN = (8192 shl RX_BUF_LEN_IDX)
|
|
|
|
struct rx_head
|
|
status dd ?
|
|
control dd ?
|
|
buff_addr dd ? ; address
|
|
next_desc dd ? ;
|
|
|
|
buff_addr_virt dd ?
|
|
rd 3 ; alignment
|
|
ends
|
|
|
|
struct tx_head
|
|
status dd ?
|
|
control dd ?
|
|
buff_addr dd ? ; address
|
|
next_desc dd ? ;
|
|
|
|
buff_addr_virt dd ?
|
|
rd 3 ; alignment
|
|
ends
|
|
|
|
struct device ETH_DEVICE
|
|
|
|
io_addr dd ?
|
|
pci_dev dd ?
|
|
pci_bus dd ?
|
|
revision db ?
|
|
irq_line db ?
|
|
chip_id dw ?
|
|
|
|
cur_rx dw ?
|
|
cur_tx dw ?
|
|
last_tx dw ?
|
|
|
|
rb 0x100 - ($ and 0xff) ; align 256
|
|
tx_ring rb sizeof.tx_head*TX_RING_SIZE
|
|
|
|
rb 0x100 - ($ and 0xff) ; align 256
|
|
rx_ring rb sizeof.rx_head*RX_RING_SIZE
|
|
|
|
ends
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; ;;
|
|
;; proc START ;;
|
|
;; ;;
|
|
;; (standard driver proc) ;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
proc START c, reason:dword, cmdline:dword
|
|
|
|
cmp [reason], DRV_ENTRY
|
|
jne .fail
|
|
|
|
DEBUGF 1,"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 aren't 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 doesn't 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 address 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 occurred, 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 occurred, remove all allocated data and exit (returning -1 in eax)
|
|
|
|
.destroy:
|
|
; todo: reset device into virgin state
|
|
|
|
.err:
|
|
invoke KernelFree, ebx
|
|
|
|
.fail:
|
|
or eax, -1
|
|
ret
|
|
|
|
;------------------------------------------------------
|
|
endp
|
|
|
|
|
|
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
|
|
;; ;;
|
|
;; Actual Hardware dependent code starts here ;;
|
|
;; ;;
|
|
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
|
|
|
|
|
|
;-------
|
|
;
|
|
; PROBE
|
|
;
|
|
;-------
|
|
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
|
|
|
|
; get device id
|
|
invoke PciRead16, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.device_id
|
|
mov [ebx + device.chip_id], ax
|
|
|
|
mov esi, chiplist
|
|
.loop:
|
|
cmp word[esi+2], ax
|
|
je .got_it
|
|
add esi, 8
|
|
cmp esi, chiplist + 6*8
|
|
jbe .loop
|
|
DEBUGF 2, "Unknown chip: 0x%x, continuing anyway\n", ax
|
|
jmp .done
|
|
.got_it:
|
|
mov eax, dword[esi+4]
|
|
mov [ebx + device.name], eax
|
|
DEBUGF 1, "Chip type = %s\n", eax
|
|
.done:
|
|
|
|
; get revision id.
|
|
invoke PciRead8, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.revision_id
|
|
mov [ebx + device.revision], al
|
|
|
|
movzx eax, [ebx + device.revision]
|
|
DEBUGF 1, "Card revision = 0x%x\n", eax
|
|
|
|
; D-Link provided reset code (with comment additions)
|
|
cmp al, 0x40
|
|
jb .below_x40
|
|
|
|
mov ax, [ebx + device.chip_id]
|
|
DEBUGF 1, "Enabling Sticky Bit Workaround for Chip_id: 0x%x\n", ax
|
|
|
|
; clear sticky bit before reset & read ethernet address
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], bySTICKHW
|
|
in al, dx
|
|
and al, 0xFC
|
|
out dx, al
|
|
|
|
; (bits written are cleared?)
|
|
; disable force PME-enable
|
|
set_io [ebx + device.io_addr], byWOLcgClr
|
|
mov al, 0x80
|
|
out dx, al
|
|
|
|
; disable power-event config bit
|
|
mov al, 0xFF
|
|
out dx, al
|
|
|
|
; clear power status (undocumented in vt6102 docs?)
|
|
set_io [ebx + device.io_addr], byPwrcsrClr
|
|
out dx, al
|
|
|
|
.below_x40:
|
|
|
|
; Reset the chip to erase previous misconfiguration.
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], byCR0
|
|
mov ax, CR_SFRST
|
|
out dx, ax
|
|
|
|
; if vt3043 delay after reset
|
|
cmp [ebx + device.revision], 0x40
|
|
jae @f
|
|
mov esi, 200 ; 2000ms
|
|
invoke Sleep
|
|
@@:
|
|
|
|
; polling till software reset complete
|
|
mov ecx, W_MAX_TIMEOUT
|
|
.poll_again:
|
|
in ax, dx
|
|
test ax, CR_SFRST
|
|
jz @f
|
|
loop .poll_again
|
|
DEBUGF 1, "Soft reset timeout!\n"
|
|
@@:
|
|
|
|
; issue AUTOLoad in EECSR to reload eeprom
|
|
set_io [ebx + device.io_addr], byEECSR
|
|
mov al, 0x20
|
|
out dx, al
|
|
|
|
; if vt3065 delay after reset
|
|
cmp [ebx + device.revision], 0x40
|
|
jb .not_vt3065
|
|
|
|
; delay 10ms to let MAC stable
|
|
mov esi, 1 ; 10ms
|
|
invoke Sleep
|
|
|
|
; for 3065D, EEPROM reloaded will cause bit 0 in MAC_REG_CFGA
|
|
; turned on. it makes MAC receive magic packet
|
|
; automatically. So, we turn it off. (D-Link)
|
|
|
|
set_io [ebx + device.io_addr], byCFGA
|
|
in al, dx
|
|
and al, 0xFE
|
|
out dx, al
|
|
|
|
; turn on bit2 in PCI configuration register 0x53 , only for 3065
|
|
invoke PciRead8, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_REG_MODE3
|
|
or al, MODE3_MIION
|
|
invoke PciWrite8, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_REG_MODE3, eax
|
|
.not_vt3065:
|
|
|
|
; back off algorithm, disable the right-most 4-bit off CFGD
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], byCFGD
|
|
in al, dx
|
|
and al, not (CFGD_RANDOM or CFGD_CFDX or CFGD_CEREN or CFGD_CETEN)
|
|
out dx, al
|
|
|
|
; reload eeprom
|
|
call reload_eeprom
|
|
|
|
; read MAC
|
|
call read_mac
|
|
|
|
; restart MII auto-negotiation
|
|
stdcall WriteMII, 0, 1 shl 9, 1
|
|
|
|
DEBUGF 1, "Analyzing Media type, this may take several seconds"
|
|
|
|
mov ecx, 5
|
|
.read_again:
|
|
mov esi, 1
|
|
invoke Sleep
|
|
|
|
stdcall ReadMII, 1
|
|
test eax, 0x0020
|
|
jnz .read_done
|
|
loop .read_again
|
|
DEBUGF 1, "timeout!\n"
|
|
.read_done:
|
|
DEBUGF 1, "OK\n"
|
|
|
|
if 0
|
|
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], 0x6C
|
|
in al, dx
|
|
and eax, 0xFF
|
|
DEBUGF 1, "MII : Address %x\n", ax
|
|
|
|
stdcall ReadMII, 1
|
|
DEBUGF 1, "status 0x%x\n", ax
|
|
|
|
stdcall ReadMII, 4
|
|
DEBUGF 1, "advertising 0x%x\n", ax
|
|
|
|
stdcall ReadMII, 5
|
|
DEBUGF 1, "link 0x%x\n", ax
|
|
|
|
end if
|
|
|
|
; query MII to know LineSpeed, duplex mode
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], MIIStatus
|
|
in al, dx
|
|
test al, MIISR_SPEED
|
|
jz .100mbps
|
|
DEBUGF 1, "Linespeed=10Mbs\n"
|
|
jmp @f
|
|
|
|
.100mbps:
|
|
DEBUGF 1, "Linespeed=100Mbs\n"
|
|
@@:
|
|
|
|
call QueryAuto
|
|
|
|
test eax, 1
|
|
jz .halfduplex
|
|
|
|
DEBUGF 1, "Fullduplex\n"
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], byCR0
|
|
mov ax, CR_FDX
|
|
out dx, ax
|
|
jmp @f
|
|
|
|
.halfduplex:
|
|
DEBUGF 1, "Halfduplex\n"
|
|
@@:
|
|
|
|
; set MII 10 FULL ON, only apply in vt3043
|
|
cmp [ebx + device.chip_id], 0x3043
|
|
jne @f
|
|
stdcall WriteMII, 0x17, 1 shl 1, 1
|
|
@@:
|
|
|
|
; turn on MII link change
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], byMIICR
|
|
in al, dx
|
|
and al, 0x7F
|
|
out dx, al
|
|
push eax
|
|
|
|
call MIIDelay
|
|
|
|
set_io [ebx + device.io_addr], byMIIAD
|
|
mov al, 0x41
|
|
out dx, al
|
|
|
|
call MIIDelay
|
|
|
|
pop eax
|
|
or al, 0x80
|
|
set_io [ebx + device.io_addr], byMIICR
|
|
out dx, al
|
|
|
|
;**************************************************************************;
|
|
;* ETH_RESET - Reset adapter *;
|
|
;**************************************************************************;
|
|
|
|
reset:
|
|
|
|
DEBUGF 1, "reset\n"
|
|
|
|
; 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
|
|
@@:
|
|
|
|
; Soft reset the chip.
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], byCR0
|
|
mov ax, CR_SFRST
|
|
out dx, ax
|
|
|
|
call MIIDelay
|
|
|
|
; Initialize rings
|
|
call init_ring
|
|
|
|
; Set Multicast
|
|
call set_rx_mode
|
|
|
|
; set TCR RCR threshold to store and forward
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], byBCR0
|
|
mov al, 0x3E
|
|
out dx, al
|
|
|
|
set_io [ebx + device.io_addr], byBCR1
|
|
mov al, 0x38
|
|
out dx, al
|
|
|
|
set_io [ebx + device.io_addr], byRCR
|
|
mov al, 0x2C
|
|
out dx, al
|
|
|
|
set_io [ebx + device.io_addr], byTCR
|
|
mov al, 0x60
|
|
out dx, al
|
|
|
|
; Set Fulldupex
|
|
|
|
call QueryAuto
|
|
test eax, eax ; full duplex?
|
|
jz @f
|
|
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], byCFGD
|
|
mov al, CFGD_CFDX
|
|
out dx, al
|
|
|
|
set_io [ebx + device.io_addr], byCR0
|
|
mov ax, CR_FDX
|
|
out dx, ax
|
|
@@:
|
|
|
|
; ENABLE interrupts
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], byIMR0
|
|
mov ax, DEFAULT_INTR
|
|
out dx, ax
|
|
|
|
; KICK NIC to WORK
|
|
|
|
set_io [ebx + device.io_addr], byCR0
|
|
in ax, dx
|
|
and ax, not CR_STOP
|
|
or ax, CR_STRT or CR_TXON or CR_RXON or CR_DPOLL
|
|
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
|
|
|
|
; say reset was successful
|
|
xor eax, eax
|
|
ret
|
|
|
|
|
|
|
|
align 4
|
|
unload:
|
|
|
|
call reset
|
|
push eax edx
|
|
DEBUGF 1, "rhine disable\n"
|
|
|
|
; Switch to loopback mode to avoid hardware races.
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], byTCR
|
|
mov al, 0x61
|
|
out dx, al
|
|
|
|
; Stop the chip's Tx and Rx processes.
|
|
set_io [ebx + device.io_addr], byCR0
|
|
mov ax, CR_STOP
|
|
out dx, ax
|
|
pop edx eax
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
align 4
|
|
reload_eeprom:
|
|
|
|
DEBUGF 1, "Reload eeprom\n"
|
|
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], byEECSR
|
|
mov al, 0x20
|
|
out dx, al
|
|
; Typically 2 cycles to reload.
|
|
mov ecx, 150
|
|
.reload:
|
|
in al, dx
|
|
test al, 0x20
|
|
jz @f
|
|
loop .reload
|
|
DEBUGF 2, "Reload eeprom: timeout!\n"
|
|
@@:
|
|
|
|
ret
|
|
|
|
|
|
|
|
; Initialize the Rx and Tx rings, along with various 'dev' bits.
|
|
align 4
|
|
init_ring:
|
|
|
|
DEBUGF 1, "Init ring\n"
|
|
|
|
lea edi, [ebx + device.rx_ring]
|
|
mov eax, edi
|
|
invoke GetPhysAddr
|
|
mov esi, eax
|
|
push esi
|
|
mov ecx, RX_RING_SIZE
|
|
.rx_init:
|
|
add esi, sizeof.rx_head
|
|
mov [edi + rx_head.status], RX_SBITS_OWN_BIT
|
|
mov [edi + rx_head.control], PKT_BUF_SZ
|
|
push ecx
|
|
invoke KernelAlloc, PKT_BUF_SZ
|
|
pop ecx
|
|
mov [edi + rx_head.buff_addr_virt], eax
|
|
invoke GetPhysAddr
|
|
mov [edi + rx_head.buff_addr], eax ; buffer ptr
|
|
mov [edi + rx_head.next_desc], esi ; next head
|
|
add edi, sizeof.rx_head
|
|
dec ecx
|
|
jnz .rx_init
|
|
pop [edi - sizeof.rx_head + rx_head.next_desc] ; Mark the last entry as wrapping the ring.
|
|
|
|
|
|
lea edi, [ebx + device.tx_ring]
|
|
mov eax, edi
|
|
invoke GetPhysAddr
|
|
mov esi, eax
|
|
push esi
|
|
mov ecx, TX_RING_SIZE
|
|
.tx_init:
|
|
add esi, sizeof.tx_head
|
|
mov [edi + tx_head.status], 0
|
|
mov [edi + tx_head.control], 0x00E08000
|
|
mov [edi + tx_head.buff_addr], 0
|
|
mov [edi + tx_head.next_desc], esi
|
|
mov [edi + tx_head.buff_addr_virt], 0
|
|
add edi, sizeof.tx_head
|
|
dec ecx
|
|
jnz .tx_init
|
|
pop [edi - sizeof.tx_head + tx_head.next_desc] ; Mark the last entry as wrapping the ring.
|
|
|
|
; write Descriptors to MAC
|
|
lea eax, [ebx + device.rx_ring]
|
|
invoke GetPhysAddr
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], dwCurrentRxDescAddr
|
|
out dx, eax
|
|
|
|
lea eax, [ebx + device.tx_ring]
|
|
invoke GetPhysAddr
|
|
set_io [ebx + device.io_addr], dwCurrentTxDescAddr
|
|
out dx, eax
|
|
|
|
xor eax, eax
|
|
mov [ebx + device.cur_rx], ax
|
|
mov [ebx + device.cur_tx], ax
|
|
mov [ebx + device.last_tx], ax
|
|
|
|
ret
|
|
|
|
|
|
align 4
|
|
QueryAuto:
|
|
|
|
DEBUGF 1, "Query Auto\n"
|
|
|
|
push ecx
|
|
stdcall ReadMII, 0x04 ; advertised
|
|
mov ecx, eax
|
|
stdcall ReadMII, 0x05
|
|
and ecx, eax
|
|
|
|
xor eax, eax
|
|
test ecx, 0x100
|
|
jnz .one
|
|
|
|
and ecx, 0x1C0
|
|
cmp ecx, 0x40
|
|
jne .zero
|
|
.one:
|
|
inc eax
|
|
DEBUGF 1, "AutoNego OK!\n"
|
|
.zero:
|
|
pop ecx
|
|
|
|
ret
|
|
|
|
|
|
proc ReadMII stdcall, byMIIIndex:dword
|
|
|
|
; DEBUGF 1, "ReadMII Index=%x\n", [byMIIIndex]
|
|
|
|
push esi ebx ecx edx
|
|
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], byMIIAD
|
|
in al, dx
|
|
mov bl, al
|
|
|
|
set_io [ebx + device.io_addr], byMIICR
|
|
in al, dx
|
|
mov bh, al
|
|
and al, 0x7F
|
|
out dx, al
|
|
|
|
call MIIDelay
|
|
|
|
mov al, byte [byMIIIndex]
|
|
set_io [ebx + device.io_addr], byMIIAD
|
|
out dx, al
|
|
|
|
call MIIDelay
|
|
|
|
set_io [ebx + device.io_addr], byMIICR
|
|
in al, dx
|
|
or al, 0x40
|
|
out dx, al
|
|
|
|
mov ecx, 200
|
|
.read_again:
|
|
in al, dx
|
|
test al, 0x40
|
|
jz @f
|
|
|
|
mov esi, 10
|
|
invoke Sleep
|
|
dec ecx
|
|
jnz .read_again
|
|
DEBUGF 2, "ReadMII: timeout!\n"
|
|
@@:
|
|
|
|
call MIIDelay
|
|
|
|
set_io [ebx + device.io_addr], byMIIAD
|
|
in ax, dx
|
|
|
|
push eax
|
|
mov ax, bx
|
|
set_io [ebx + device.io_addr], byMIIAD
|
|
out dx, al
|
|
|
|
shr ax, 8
|
|
set_io [ebx + device.io_addr], byMIICR
|
|
out dx, al
|
|
|
|
call MIIDelay
|
|
|
|
pop eax
|
|
and eax, 0xFFFF
|
|
rol ax, 8
|
|
|
|
pop edx ecx ebx esi
|
|
ret
|
|
endp
|
|
|
|
|
|
|
|
proc WriteMII stdcall, byMIISetByte:dword, byMIISetBit:dword, byMIIOP:dword
|
|
|
|
; DEBUGF 1, "WriteMII SetByte=%x SetBit=%x OP=%x\n", [byMIISetByte], [byMIISetBit], [byMIIOP]
|
|
|
|
push ebx eax ecx edx
|
|
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], byMIIAD
|
|
in al, dx
|
|
mov bl, al
|
|
|
|
set_io [ebx + device.io_addr], byMIICR
|
|
in al, dx
|
|
mov bh, al
|
|
and al, 0x7F
|
|
out dx, al
|
|
|
|
call MIIDelay
|
|
|
|
mov al, byte [byMIISetByte]
|
|
set_io [ebx + device.io_addr], byMIIAD
|
|
out dx, al
|
|
|
|
call MIIDelay
|
|
|
|
set_io [ebx + device.io_addr], byMIICR
|
|
in al, dx
|
|
or al, 0x40
|
|
out dx, al
|
|
|
|
mov ecx, 200
|
|
.read_again0:
|
|
in al, dx
|
|
test al, 0x40
|
|
jz .done
|
|
|
|
mov esi, 10
|
|
invoke Sleep
|
|
dec ecx
|
|
jnz .read_again0
|
|
DEBUGF 2, "WriteMII: timeout (1)\n"
|
|
.done:
|
|
|
|
call MIIDelay
|
|
|
|
set_io [ebx + device.io_addr], wMIIDATA
|
|
in ax, dx
|
|
|
|
mov ecx, [byMIISetBit]
|
|
rol cx, 8
|
|
|
|
cmp byte [byMIIOP], 0
|
|
jne @f
|
|
not ecx
|
|
and ax, cx
|
|
jmp .end_mascarad
|
|
@@:
|
|
or ax, cx
|
|
.end_mascarad:
|
|
|
|
set_io [ebx + device.io_addr], wMIIDATA
|
|
out dx, ax
|
|
|
|
call MIIDelay
|
|
|
|
set_io [ebx + device.io_addr], byMIICR
|
|
in al, dx
|
|
or al, 0x20
|
|
out dx, al
|
|
|
|
mov ecx, 200
|
|
.read_again1:
|
|
in al, dx
|
|
test al, 0x20
|
|
jz @f
|
|
|
|
mov esi, 10
|
|
invoke Sleep
|
|
dec ecx
|
|
jnz .read_again1
|
|
DEBUGF 2, "WriteMII timeout (2)\n"
|
|
@@:
|
|
|
|
call MIIDelay
|
|
|
|
mov ax, bx
|
|
and al, 0x7F
|
|
set_io [ebx + device.io_addr], byMIIAD
|
|
out dx, al
|
|
|
|
shr ax, 8
|
|
set_io [ebx + device.io_addr], byMIICR
|
|
out dx, al
|
|
|
|
call MIIDelay
|
|
|
|
pop edx ecx eax ebx
|
|
ret
|
|
endp
|
|
|
|
|
|
align 4
|
|
MIIDelay:
|
|
|
|
mov ecx, 0x7FFF
|
|
@@:
|
|
in al, 0x61
|
|
in al, 0x61
|
|
in al, 0x61
|
|
in al, 0x61
|
|
loop @b
|
|
|
|
ret
|
|
|
|
|
|
align 4
|
|
set_rx_mode:
|
|
|
|
DEBUGF 1, "Set RX mode\n"
|
|
|
|
; ! IFF_PROMISC
|
|
mov eax, 0xffffffff
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], byMAR0
|
|
out dx, eax
|
|
|
|
set_io [ebx + device.io_addr], byMAR4
|
|
out dx, eax
|
|
|
|
set_io [ebx + device.io_addr], byRCR
|
|
mov al, 0x6C ;rx_mode = 0x0C;
|
|
out dx, al ;outb(0x60 /* thresh */ | rx_mode, byRCR );
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
|
|
; Beware of PCI posted writes
|
|
macro IOSYNC
|
|
{
|
|
set_io [ebx + device.io_addr], StationAddr
|
|
in al, dx
|
|
}
|
|
|
|
|
|
|
|
align 4
|
|
read_mac:
|
|
|
|
lea edi, [ebx + device.mac]
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], byPAR0
|
|
mov ecx, 6
|
|
.next:
|
|
in al, dx
|
|
stosb
|
|
inc edx
|
|
dec ecx
|
|
jnz .next
|
|
|
|
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 ;;
|
|
;; ;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
align 4
|
|
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
|
|
|
|
movzx eax, [ebx + device.cur_tx]
|
|
mov ecx, sizeof.tx_head
|
|
mul ecx
|
|
lea edi, [ebx + device.tx_ring]
|
|
add edi, eax
|
|
|
|
cmp [edi + tx_head.buff_addr_virt], 0
|
|
jne .fail
|
|
|
|
mov eax, [bufferptr]
|
|
mov [edi + tx_head.buff_addr_virt], eax
|
|
invoke GetPhysAddr
|
|
mov [edi + tx_head.buff_addr], eax
|
|
mov ecx, [buffersize]
|
|
and ecx, TX_CBITS_TX_BUF_SIZE
|
|
or ecx, 0x00E08000
|
|
mov [edi + tx_head.control], ecx
|
|
or [edi + tx_head.status], TX_SBITS_OWN_BIT
|
|
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], byCR1
|
|
in al, dx
|
|
or al, CR1_TDMD1
|
|
out dx, al
|
|
|
|
inc [ebx + device.cur_tx]
|
|
and [ebx + device.cur_tx], TX_RING_SIZE-1
|
|
|
|
;outw(IMRShadow,byIMR0); ;
|
|
|
|
; Update stats
|
|
inc [ebx + device.packets_tx]
|
|
mov ecx, [buffersize]
|
|
add dword [ebx + device.bytes_tx], ecx
|
|
adc dword [ebx + device.bytes_tx + 4], 0
|
|
|
|
DEBUGF 1,"Transmit OK\n"
|
|
popf
|
|
xor eax, eax
|
|
ret
|
|
|
|
.fail:
|
|
DEBUGF 2,"Transmit 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 which made IRQ occur
|
|
|
|
mov ecx, [devices]
|
|
test ecx, ecx
|
|
jz .nothing
|
|
mov esi, device_list
|
|
.nextdevice:
|
|
mov ebx, [esi]
|
|
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], IntrStatus
|
|
in ax, dx
|
|
out dx, ax ; send it back to ACK
|
|
test ax, ax
|
|
jnz .got_it
|
|
.continue:
|
|
add esi, 4
|
|
dec ecx
|
|
jnz .nextdevice
|
|
.nothing:
|
|
pop edi esi ebx
|
|
xor eax, eax
|
|
|
|
ret ; If no device was found, abort (The irq was probably for a device, not registered to this driver)
|
|
|
|
|
|
|
|
.got_it:
|
|
|
|
DEBUGF 1, "status=0x%x\n", ax
|
|
|
|
push ax
|
|
|
|
test ax, IntrRxDone
|
|
jz .not_RX
|
|
|
|
push ebx
|
|
.more_RX:
|
|
pop ebx
|
|
|
|
; Get the current descriptor pointer
|
|
|
|
movzx eax, [ebx + device.cur_rx]
|
|
mov ecx, sizeof.rx_head
|
|
mul ecx
|
|
lea edi, [ebx + device.rx_ring]
|
|
add edi, eax
|
|
|
|
; Check it's status
|
|
|
|
test [edi + rx_head.status], RX_SBITS_OWN_BIT
|
|
jnz .not_bit_own
|
|
|
|
DEBUGF 1, "Packet status = 0x%x\n", [edi + rx_head.status]
|
|
|
|
; TODO: check error bits
|
|
|
|
; get length
|
|
|
|
mov ecx, [edi + rx_head.status]
|
|
and ecx, RX_SBITS_FRAME_LENGTH
|
|
shr ecx, 16
|
|
sub ecx, 4 ; We dont want CRC
|
|
|
|
; Update stats
|
|
|
|
add dword [ebx + device.bytes_rx], ecx
|
|
adc dword [ebx + device.bytes_rx + 4], 0
|
|
inc [ebx + device.packets_rx]
|
|
|
|
; Push packet size and pointer, kernel will need it..
|
|
|
|
push ebx
|
|
push .more_RX ; return ptr
|
|
|
|
push ecx ; full packet size
|
|
push [edi + rx_head.buff_addr_virt]
|
|
|
|
; reset the RX descriptor
|
|
|
|
push edi
|
|
invoke KernelAlloc, PKT_BUF_SZ
|
|
pop edi
|
|
mov [edi + rx_head.buff_addr_virt], eax
|
|
invoke GetPhysAddr
|
|
mov [edi + rx_head.buff_addr], eax
|
|
mov [edi + rx_head.status], RX_SBITS_OWN_BIT
|
|
|
|
; 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 [Eth_input]
|
|
|
|
.not_bit_own:
|
|
.not_RX:
|
|
|
|
pop ax
|
|
|
|
test ax, IntrTxDone
|
|
jz .not_TX
|
|
|
|
.loop_tx:
|
|
movzx eax, [ebx + device.last_tx]
|
|
mov ecx, sizeof.tx_head
|
|
mul ecx
|
|
lea edi, [ebx + device.tx_ring]
|
|
add edi, eax
|
|
|
|
test [edi + tx_head.status], TX_SBITS_OWN_BIT
|
|
jnz .not_TX
|
|
|
|
cmp [edi + tx_head.buff_addr_virt], 0
|
|
je .not_TX
|
|
|
|
DEBUGF 1,"Freeing buffer 0x%x\n", [edi + tx_head.buff_addr_virt]
|
|
|
|
push [edi + tx_head.buff_addr_virt]
|
|
mov [edi + tx_head.buff_addr_virt], 0
|
|
invoke KernelFree
|
|
|
|
inc [ebx + device.last_tx]
|
|
and [ebx + device.last_tx], TX_RING_SIZE - 1
|
|
|
|
jmp .loop_tx
|
|
|
|
.not_TX:
|
|
|
|
; On Rhine-II, Bit 3 indicates Tx descriptor write-back race.
|
|
if 0
|
|
cmp [ebx + device.chip_id], 0x3065 ;if (tp->chip_id == 0x3065)
|
|
jne @f
|
|
push ax
|
|
xor eax, eax
|
|
set_io [ebx + device.io_addr], IntrStatus2
|
|
in al, dx ; intr_status |= inb(nic->ioaddr + IntrStatus2) << 16;
|
|
shl eax, 16
|
|
pop ax
|
|
@@:
|
|
end if
|
|
|
|
if 0
|
|
|
|
; Acknowledge all of the current interrupt sources ASAP.
|
|
xor ecx, ecx
|
|
test eax, IntrTxDescRace
|
|
jz @f
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], IntrStatus2
|
|
push ax
|
|
mov al, 0x08
|
|
out dx, al
|
|
pop ax
|
|
@@:
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], IntrStatus
|
|
out dx, ax
|
|
IOSYNC
|
|
|
|
end if
|
|
|
|
pop edi esi ebx
|
|
xor eax, eax
|
|
inc eax
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
|
|
; End of code
|
|
|
|
data fixups
|
|
end data
|
|
|
|
include '../peimport.inc'
|
|
|
|
my_service db 'RHINE',0 ; max 16 chars including zero
|
|
|
|
chiplist:
|
|
dd 0x30431106, rhine_3043;, RHINE_IOTYPE, RHINE_I_IOSIZE, CanHaveMII or ReqTxAlign or HasV1TxStat
|
|
dd 0x61001106, rhine_6100;, RHINE_IOTYPE, RHINE_I_IOSIZE, CanHaveMII or ReqTxAlign or HasV1TxStat
|
|
dd 0x30651106, rhine_6102;, RHINE_IOTYPE, RHINEII_IOSIZE, CanHaveMII or HasWOL
|
|
dd 0x31061106, rhine_6105;, RHINE_IOTYPE, RHINEII_IOSIZE, CanHaveMII or HasWOL
|
|
; Duplicate entry, with 'M' features enabled.
|
|
; dd 0x31061106, rhine_6105;, RHINE_IOTYPE, RHINEII_IOSIZE, CanHaveMII or HasWOL or HasIPChecksum or HasVLAN
|
|
dd 0x30531106, rhine_3053;, RHINE_IOTYPE, RHINEII_IOSIZE, CanHaveMII or HasWOL
|
|
dd 0
|
|
|
|
rhine_3043 db "VIA VT3043 Rhine", 0
|
|
rhine_6100 db "VIA VT86C100A Rhine", 0
|
|
rhine_6102 db "VIA VT6102 Rhine-II", 0
|
|
rhine_6105 db "VIA VT6105LOM Rhine-III (3106)", 0
|
|
rhine_3053 db "VIA VT6105M Rhine-III (3053 prototype)", 0
|
|
|
|
include_debug_strings ; All data which 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
|
|
|