forked from KolibriOS/kolibrios
fed8d22166
git-svn-id: svn://kolibrios.org@9149 a494cfbc-eb01-0410-851d-a64ba20cac60
1699 lines
46 KiB
NASM
1699 lines
46 KiB
NASM
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; ;;
|
|
;; Copyright (C) KolibriOS team 2004-2021. All rights reserved. ;;
|
|
;; Distributed under terms of the GNU General Public License ;;
|
|
;; ;;
|
|
;; AMD PCnet driver for KolibriOS ;;
|
|
;; ;;
|
|
;; By hidnplayr & clevermouse ;;
|
|
;; ;;
|
|
;; Based on the PCnet32 driver for MenuetOS, by Jarek Pelczar ;;
|
|
;; ;;
|
|
;; GNU GENERAL PUBLIC LICENSE ;;
|
|
;; Version 2, June 1991 ;;
|
|
;; ;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
format PE DLL native
|
|
entry START
|
|
|
|
CURRENT_API = 0x0200
|
|
COMPATIBLE_API = 0x0100
|
|
API_VERSION = (COMPATIBLE_API shl 16) + CURRENT_API
|
|
|
|
; configureable area
|
|
|
|
MAX_DEVICES = 16 ; Maximum number of devices this driver may handle
|
|
|
|
__DEBUG__ = 1 ; 1 = on, 0 = off
|
|
__DEBUG_LEVEL__ = 2 ; 1 = verbose, 2 = errors only
|
|
|
|
TX_RING_SIZE = 32 ; Number of packets in send ring buffer
|
|
RX_RING_SIZE = 32 ; Number of packets in receive ring buffer
|
|
|
|
; end configureable area
|
|
|
|
section '.flat' readable writable executable
|
|
|
|
include '../struct.inc'
|
|
include '../macros.inc'
|
|
include '../proc32.inc'
|
|
include '../fdo.inc'
|
|
include '../netdrv.inc'
|
|
|
|
if (bsr TX_RING_SIZE)>(bsf TX_RING_SIZE)
|
|
display 'TX_RING_SIZE must be a power of two'
|
|
err
|
|
end if
|
|
|
|
if (bsr RX_RING_SIZE)>(bsf RX_RING_SIZE)
|
|
display 'RX_RING_SIZE must be a power of two'
|
|
err
|
|
end if
|
|
|
|
PORT_AUI = 0x00
|
|
PORT_10BT = 0x01
|
|
PORT_GPSI = 0x02
|
|
PORT_MII = 0x03
|
|
PORT_PORTSEL = 0x03
|
|
PORT_ASEL = 0x04
|
|
PORT_100 = 0x40
|
|
PORT_FD = 0x80
|
|
|
|
DMA_MASK = 0xffffffff
|
|
|
|
TX_RING_LEN_BITS = ((bsf TX_RING_SIZE) shl 12)
|
|
RX_RING_LEN_BITS = ((bsf RX_RING_SIZE) shl 4)
|
|
|
|
PKT_BUF_SZ = 1544
|
|
|
|
WIO_RDP = 0x10
|
|
WIO_RAP = 0x12
|
|
WIO_RESET = 0x14
|
|
WIO_BDP = 0x16
|
|
|
|
DWIO_RDP = 0x10
|
|
DWIO_RAP = 0x14
|
|
DWIO_RESET = 0x18
|
|
DWIO_BDP = 0x1C
|
|
|
|
; CSR registers
|
|
|
|
CSR_CSR = 0x00
|
|
CSR_IAB0 = 0x01
|
|
CSR_IAB1 = 0x02
|
|
CSR_IMR = 0x03
|
|
CSR_TFEAT = 0x04
|
|
CSR_EXTCTL1 = 0x05
|
|
CSR_DTBLLEN = 0x06
|
|
CSR_EXTCTL2 = 0x07
|
|
CSR_MAR0 = 0x08
|
|
CSR_MAR1 = 0x09
|
|
CSR_MAR2 = 0x0A
|
|
CSR_MAR3 = 0x0B
|
|
CSR_PAR0 = 0x0C
|
|
CSR_PAR1 = 0x0D
|
|
CSR_PAR2 = 0x0E
|
|
CSR_MODE = 0x0F
|
|
CSR_RXADDR0 = 0x18
|
|
CSR_RXADDR1 = 0x19
|
|
CSR_TXADDR0 = 0x1E
|
|
CSR_TXADDR1 = 0x1F
|
|
CSR_TXPOLL = 0x2F
|
|
CSR_RXPOLL = 0x31
|
|
CSR_RXRINGLEN = 0x4C
|
|
CSR_TXRINGLEN = 0x4E
|
|
CSR_DMACTL = 0x50
|
|
CSR_BUSTIMER = 0x52
|
|
CSR_MEMERRTIMEO = 0x64
|
|
CSR_ONNOWMISC = 0x74
|
|
CSR_ADVFEAT = 0x7A
|
|
CSR_MACCFG = 0x7D
|
|
CSR_CHIPID0 = 0x58
|
|
CSR_CHIPID1 = 0x59
|
|
|
|
; Control and Status Register (CSR0)
|
|
|
|
CSR_INIT = 0x0001 ;1 shl 0
|
|
CSR_START = 0x0002 ;1 shl 1
|
|
CSR_STOP = 0x0004 ;1 shl 2
|
|
CSR_TX = 0x0008 ;1 shl 3
|
|
CSR_TXON = 0x0010 ;1 shl 4
|
|
CSR_RXON = 0x0020 ;1 shl 5
|
|
CSR_INTEN = 0x0040 ;1 shl 6
|
|
CSR_INTR = 0x0080 ;1 shl 7
|
|
CSR_IDONE = 0x0100 ;1 shl 8
|
|
CSR_TINT = 0x0200 ;1 shl 9
|
|
CSR_RINT = 0x0400 ;1 shl 10
|
|
CSR_MERR = 0x0800 ;1 shl 11
|
|
CSR_MISS = 0x1000 ;1 shl 12
|
|
CSR_CERR = 0x2000 ;1 shl 13
|
|
|
|
; Interrupt masks and deferral control (CSR3)
|
|
|
|
IMR_BSWAP = 0x0004
|
|
IMR_ENMBA = 0x0008 ; enable modified backoff alg
|
|
IMR_DXMT2PD = 0x0010
|
|
IMR_LAPPEN = 0x0020 ; lookahead packet processing enb
|
|
IMR_DXSUFLO = 0x0040 ; disable TX stop on underflow
|
|
IMR_IDONE = 0x0100
|
|
IMR_TINT = 0x0200
|
|
IMR_RINT = 0x0400
|
|
IMR_MERR = 0x0800
|
|
IMR_MISS = 0x1000
|
|
|
|
; Masked interrupts will be disabled.
|
|
IMR = 0 ;IMR_IDONE ;or IMR_TINT or IMR_RINT or IMR_MERR or IMR_MISS
|
|
|
|
; Test and features control (CSR4)
|
|
|
|
TFEAT_TXSTRTMASK = 0x0004
|
|
TFEAT_TXSTRT = 0x0008
|
|
TFEAT_RXCCOFLOWM = 0x0010 ; Rx collision counter oflow
|
|
TFEAT_RXCCOFLOW = 0x0020
|
|
TFEAT_UINT = 0x0040
|
|
TFEAT_UINTREQ = 0x0080
|
|
TFEAT_MISSOFLOWM = 0x0100
|
|
TFEAT_MISSOFLOW = 0x0200
|
|
TFEAT_STRIP_FCS = 0x0400
|
|
TFEAT_PAD_TX = 0x0800
|
|
TFEAT_TXDPOLL = 0x1000
|
|
TFEAT_DMAPLUS = 0x4000
|
|
|
|
; Extended control and interrupt 1 (CSR5)
|
|
|
|
EXTCTL1_SPND = 0x0001 ; suspend
|
|
EXTCTL1_MPMODE = 0x0002 ; magic packet mode
|
|
EXTCTL1_MPENB = 0x0004 ; magic packet enable
|
|
EXTCTL1_MPINTEN = 0x0008 ; magic packet interrupt enable
|
|
EXTCTL1_MPINT = 0x0010 ; magic packet interrupt
|
|
EXTCTL1_MPPLBA = 0x0020 ; magic packet phys. logical bcast
|
|
EXTCTL1_EXDEFEN = 0x0040 ; excessive deferral interrupt enb.
|
|
EXTCTL1_EXDEF = 0x0080 ; excessive deferral interrupt
|
|
EXTCTL1_SINTEN = 0x0400 ; system interrupt enable
|
|
EXTCTL1_SINT = 0x0800 ; system interrupt
|
|
EXTCTL1_LTINTEN = 0x4000 ; last TX interrupt enb
|
|
EXTCTL1_TXOKINTD = 0x8000 ; TX OK interrupt disable
|
|
|
|
; RX/TX descriptor len (CSR6)
|
|
|
|
DTBLLEN_RLEN = 0x0F00
|
|
DTBLLEN_TLEN = 0xF000
|
|
|
|
; Extended control and interrupt 2 (CSR7)
|
|
|
|
EXTCTL2_MIIPDTINTE = 0x0001
|
|
EXTCTL2_MIIPDTINT = 0x0002
|
|
EXTCTL2_MCCIINTE = 0x0004
|
|
EXTCTL2_MCCIINT = 0x0008
|
|
EXTCTL2_MCCINTE = 0x0010
|
|
EXTCTL2_MCCINT = 0x0020
|
|
EXTCTL2_MAPINTE = 0x0040
|
|
EXTCTL2_MAPINT = 0x0080
|
|
EXTCTL2_MREINTE = 0x0100
|
|
EXTCTL2_MREINT = 0x0200
|
|
EXTCTL2_STINTE = 0x0400
|
|
EXTCTL2_STINT = 0x0800
|
|
EXTCTL2_RXDPOLL = 0x1000
|
|
EXTCTL2_RDMD = 0x2000
|
|
EXTCTL2_RXFRTG = 0x4000
|
|
EXTCTL2_FASTSPNDE = 0x8000
|
|
|
|
; Mode (CSR15)
|
|
|
|
MODE_RXD = 0x0001 ; RX disable
|
|
MODE_TXD = 0x0002 ; TX disable
|
|
MODE_LOOP = 0x0004 ; loopback enable
|
|
MODE_TXCRCD = 0x0008
|
|
MODE_FORCECOLL = 0x0010
|
|
MODE_RETRYD = 0x0020
|
|
MODE_INTLOOP = 0x0040
|
|
MODE_PORTSEL = 0x0180
|
|
MODE_RXVPAD = 0x2000
|
|
MODE_RXNOBROAD = 0x4000
|
|
MODE_PROMISC = 0x8000
|
|
|
|
; BCR (Bus Control Registers)
|
|
|
|
BCR_MMRA = 0x00 ; Master Mode Read Active
|
|
BCR_MMW = 0x01 ; Master Mode Write Active
|
|
BCR_MISCCFG = 0x02
|
|
BCR_LED0 = 0x04
|
|
BCR_LED1 = 0x05
|
|
BCR_LED2 = 0x06
|
|
BCR_LED3 = 0x07
|
|
BCR_DUPLEX = 0x09
|
|
BCR_BUSCTL = 0x12
|
|
BCR_EECTL = 0x13
|
|
BCR_SSTYLE = 0x14
|
|
BCR_PCILAT = 0x16
|
|
BCR_PCISUBVENID = 0x17
|
|
BCR_PCISUBSYSID = 0x18
|
|
BCR_SRAMSIZE = 0x19
|
|
BCR_SRAMBOUND = 0x1A
|
|
BCR_SRAMCTL = 0x1B
|
|
BCR_MIICTL = 0x20
|
|
BCR_MIIADDR = 0x21
|
|
BCR_MIIDATA = 0x22
|
|
BCR_PCIVENID = 0x23
|
|
BCR_PCIPCAP = 0x24
|
|
BCR_DATA0 = 0x25
|
|
BCR_DATA1 = 0x26
|
|
BCR_DATA2 = 0x27
|
|
BCR_DATA3 = 0x28
|
|
BCR_DATA4 = 0x29
|
|
BCR_DATA5 = 0x2A
|
|
BCR_DATA6 = 0x2B
|
|
BCR_DATA7 = 0x2C
|
|
BCR_ONNOWPAT0 = 0x2D
|
|
BCR_ONNOWPAT1 = 0x2E
|
|
BCR_ONNOWPAT2 = 0x2F
|
|
BCR_PHYSEL = 0x31
|
|
|
|
; RX status register
|
|
|
|
RXSTAT_BPE = 0x0080 ; bus parity error
|
|
RXSTAT_ENP = 0x0100 ; end of packet
|
|
RXSTAT_STP = 0x0200 ; start of packet
|
|
RXSTAT_BUFF = 0x0400 ; buffer error
|
|
RXSTAT_CRC = 0x0800 ; CRC error
|
|
RXSTAT_OFLOW = 0x1000 ; rx overrun
|
|
RXSTAT_FRAM = 0x2000 ; framing error
|
|
RXSTAT_ERR = 0x4000 ; error summary
|
|
RXSTAT_OWN = 0x8000
|
|
|
|
; TX status register
|
|
|
|
TXSTAT_TRC = 0x0000000F ; transmit retries
|
|
TXSTAT_RTRY = 0x04000000 ; retry
|
|
TXSTAT_LCAR = 0x08000000 ; lost carrier
|
|
TXSTAT_LCOL = 0x10000000 ; late collision
|
|
TXSTAT_EXDEF = 0x20000000 ; excessive deferrals
|
|
TXSTAT_UFLOW = 0x40000000 ; transmit underrun
|
|
TXSTAT_BUFF = 0x80000000 ; buffer error
|
|
|
|
TXCTL_OWN = 0x8000
|
|
TXCTL_ERR = 0x4000 ; error summary
|
|
TXCTL_ADD_FCS = 0x2000 ; add FCS to pkt
|
|
TXCTL_MORE_LTINT = 0x1000
|
|
TXCTL_ONE = 0x0800
|
|
TXCTL_DEF = 0x0400
|
|
TXCTL_STP = 0x0200
|
|
TXCTL_ENP = 0x0100
|
|
TXCTL_BPE = 0x0080
|
|
|
|
TXCTL_MBO = 0x0000F000
|
|
TXCTL_BUFSZ = 0x00000FFF
|
|
|
|
MAX_PHYS = 32
|
|
|
|
|
|
; Pcnet configuration structure
|
|
struct pcnet_init_block
|
|
|
|
mode dw ?
|
|
tlen_rlen dw ?
|
|
phys_addr dp ?
|
|
reserved dw ?
|
|
filter dq ?
|
|
rx_ring_phys dd ?
|
|
tx_ring_phys dd ?
|
|
ends
|
|
|
|
|
|
struct device ETH_DEVICE
|
|
|
|
rb 0x100-($ and 0xff) ; align 256
|
|
init_block pcnet_init_block
|
|
|
|
rb 0x100-($ and 0xff) ; align 256
|
|
rx_ring rb RX_RING_SIZE * sizeof.descriptor
|
|
|
|
rb 0x100-($ and 0xff) ; align 256
|
|
tx_ring rb TX_RING_SIZE * sizeof.descriptor
|
|
|
|
cur_rx dd ?
|
|
cur_tx dd ?
|
|
last_tx dd ?
|
|
|
|
options dd ?
|
|
full_duplex db ?
|
|
chip_version dw ?
|
|
mii db ?
|
|
ltint db ?
|
|
dxsuflo db ?
|
|
fset db ?
|
|
fdx db ?
|
|
|
|
io_addr dd ?
|
|
irq_line db ?
|
|
pci_bus dd ?
|
|
pci_dev dd ?
|
|
|
|
phy dw ?
|
|
|
|
link_timer dd ?
|
|
|
|
rb 0x100-($ and 0xff) ; align 256
|
|
|
|
read_csr dd ?
|
|
write_csr dd ?
|
|
read_bcr dd ?
|
|
write_bcr dd ?
|
|
read_rap dd ?
|
|
write_rap dd ?
|
|
sw_reset dd ?
|
|
|
|
ends
|
|
|
|
struct descriptor
|
|
|
|
base dd ?
|
|
length dw ?
|
|
status dw ?
|
|
msg_length dw ?
|
|
misc dw ?
|
|
virtual dd ?
|
|
|
|
ends
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; ;;
|
|
;; proc START ;;
|
|
;; ;;
|
|
;; (standard driver proc) ;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
proc START c, reason:dword, cmdline:dword
|
|
|
|
cmp [reason], DRV_ENTRY
|
|
jne .fail
|
|
|
|
DEBUGF 2,"Loading driver\n"
|
|
invoke RegService, my_service, service_proc
|
|
ret
|
|
|
|
.fail:
|
|
xor eax, eax
|
|
ret
|
|
|
|
endp
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; ;;
|
|
;; proc SERVICE_PROC ;;
|
|
;; ;;
|
|
;; (standard driver proc) ;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
align 4
|
|
proc service_proc stdcall, ioctl:dword
|
|
|
|
mov edx, [ioctl]
|
|
mov eax, [edx + IOCTL.io_code]
|
|
|
|
;------------------------------------------------------
|
|
|
|
cmp eax, 0 ;SRV_GETVERSION
|
|
jne @F
|
|
|
|
cmp [edx + IOCTL.out_size], 4
|
|
jb .fail
|
|
mov eax, [edx + IOCTL.output]
|
|
mov [eax], dword API_VERSION
|
|
|
|
xor eax, eax
|
|
ret
|
|
|
|
;------------------------------------------------------
|
|
@@:
|
|
cmp eax, 1 ;SRV_HOOK
|
|
jne .fail
|
|
|
|
cmp [edx + IOCTL.inp_size], 3 ; Data input must be at least 3 bytes
|
|
jb .fail
|
|
|
|
mov eax, [edx + IOCTL.input]
|
|
cmp byte[eax], 1 ; 1 means device number and bus number (pci) are given
|
|
jne .fail ; other types arent supported for this card yet
|
|
|
|
; check if the device is already listed
|
|
|
|
mov ecx, [devices]
|
|
test ecx, ecx
|
|
jz .firstdevice
|
|
|
|
mov esi, device_list
|
|
; mov eax, [edx + IOCTL.input] ; get the pci bus and device numbers
|
|
mov ax, [eax+1] ;
|
|
.nextdevice:
|
|
mov ebx, [esi]
|
|
cmp al, byte[ebx + device.pci_bus]
|
|
jne @f
|
|
cmp ah, byte[ebx + device.pci_dev]
|
|
je .find_devicenum ; Device is already loaded, let's find it's device number
|
|
@@:
|
|
add esi, 4
|
|
loop .nextdevice
|
|
|
|
; This device doesnt have its own eth_device structure yet, lets create one
|
|
|
|
.firstdevice:
|
|
cmp [devices], MAX_DEVICES ; First check if the driver can handle one more card
|
|
jae .fail
|
|
|
|
allocate_and_clear ebx, sizeof.device, .fail
|
|
|
|
; 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]:4
|
|
|
|
; Ok, the eth_device structure is ready, let's probe the device
|
|
; Because initialization fires IRQ, IRQ handler must be aware of this 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 .destroy ; If an error occured, exit
|
|
|
|
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
|
|
add eax, NET_BUFF.data
|
|
dec [devices]
|
|
.err:
|
|
DEBUGF 2,"Error, removing all data !\n"
|
|
invoke KernelFree, ebx
|
|
|
|
.fail:
|
|
or eax, -1
|
|
ret
|
|
|
|
;------------------------------------------------------
|
|
endp
|
|
|
|
|
|
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
|
|
;; ;;
|
|
;; Actual Hardware dependent code starts here ;;
|
|
;; ;;
|
|
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
|
|
|
|
align 4
|
|
unload:
|
|
|
|
cmp [ebx + device.link_timer], 0
|
|
je @f
|
|
invoke CancelTimerHS, [ebx + device.link_timer]
|
|
@@:
|
|
|
|
; 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 a PCnet device)
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
align 4
|
|
probe:
|
|
|
|
mov edx, [ebx + device.io_addr]
|
|
|
|
call wio_reset
|
|
|
|
xor ecx, ecx
|
|
call wio_read_csr
|
|
cmp eax, 4
|
|
jne .try_dwio
|
|
|
|
; Try Word I/O
|
|
mov ax, 88
|
|
add edx, WIO_RAP
|
|
out dx, ax
|
|
nop
|
|
nop
|
|
in ax, dx
|
|
sub edx, WIO_RAP
|
|
cmp ax, 88
|
|
jne .try_dwio
|
|
|
|
call switch_to_wio
|
|
|
|
jmp .L1
|
|
|
|
.try_dwio:
|
|
call dwio_reset
|
|
|
|
xor ecx, ecx
|
|
call dwio_read_csr
|
|
cmp eax, 4
|
|
jne .no_dev
|
|
|
|
; Try Dword I/O
|
|
add edx, DWIO_RAP
|
|
mov eax, 88
|
|
out dx, eax
|
|
nop
|
|
nop
|
|
in eax, dx
|
|
sub edx, DWIO_RAP
|
|
and eax, 0xffff
|
|
cmp eax, 88
|
|
jne .no_dev
|
|
|
|
call switch_to_dwio
|
|
|
|
jmp .L1
|
|
|
|
.no_dev:
|
|
DEBUGF 2,"device not found!\n"
|
|
mov eax, 1
|
|
ret
|
|
|
|
.L1:
|
|
mov ecx, CSR_CHIPID0
|
|
call [ebx + device.read_csr]
|
|
|
|
mov esi, eax
|
|
shr esi, 12
|
|
|
|
and ax, 0xfff
|
|
cmp ax, 3
|
|
jne .no_dev
|
|
|
|
mov ecx, CSR_CHIPID1
|
|
call [ebx + device.read_csr]
|
|
shl eax, 4
|
|
or eax, esi
|
|
mov [ebx + device.chip_version], ax
|
|
|
|
mov [ebx + device.fdx], 0
|
|
mov [ebx + device.mii], 0
|
|
mov [ebx + device.fset], 0
|
|
mov [ebx + device.dxsuflo], 0
|
|
mov [ebx + device.ltint], 0
|
|
|
|
cmp ax, 0x2420
|
|
je .L2
|
|
cmp ax, 0x2430
|
|
je .L2
|
|
|
|
mov [ebx + device.fdx], 1
|
|
|
|
cmp ax, 0x2621
|
|
je .L4
|
|
cmp ax, 0x2623
|
|
je .L5
|
|
cmp ax, 0x2624
|
|
je .L6
|
|
cmp ax, 0x2625
|
|
je .L7
|
|
cmp ax, 0x2626
|
|
je .L8
|
|
cmp ax, 0x2627
|
|
je .L9
|
|
|
|
DEBUGF 2,"Invalid chip rev\n"
|
|
jmp .no_dev
|
|
.L2:
|
|
mov [ebx + device.name], device_l2
|
|
jmp .L10
|
|
.L4:
|
|
mov [ebx + device.name], device_l4
|
|
; mov [ebx + device.fdx], 1
|
|
jmp .L10
|
|
.L5:
|
|
mov [ebx + device.name], device_l5
|
|
; mov [ebx + device.fdx], 1
|
|
mov [ebx + device.mii], 1
|
|
mov [ebx + device.fset], 1
|
|
mov [ebx + device.ltint], 1
|
|
jmp .L10
|
|
.L6:
|
|
mov [ebx + device.name], device_l6
|
|
; mov [ebx + device.fdx], 1
|
|
mov [ebx + device.mii], 1
|
|
mov [ebx + device.fset], 1
|
|
jmp .L10
|
|
.L7:
|
|
mov [ebx + device.name], device_l7
|
|
; mov [ebx + device.fdx], 1
|
|
mov [ebx + device.mii], 1
|
|
jmp .L10
|
|
.L8:
|
|
mov [ebx + device.name], device_l8
|
|
; mov [ebx + device.fdx], 1
|
|
mov ecx, CSR_RXPOLL
|
|
call dword [ebx + device.read_bcr]
|
|
call dword [ebx + device.write_bcr]
|
|
jmp .L10
|
|
.L9:
|
|
mov [ebx + device.name], device_l9
|
|
; mov [ebx + device.fdx], 1
|
|
mov [ebx + device.mii], 1
|
|
.L10:
|
|
DEBUGF 1,"device name: %s\n", [ebx + device.name]
|
|
|
|
cmp [ebx + device.fset], 1
|
|
jne .L11
|
|
mov ecx, BCR_BUSCTL
|
|
call [ebx + device.read_bcr]
|
|
or eax, 0x800
|
|
call [ebx + device.write_bcr]
|
|
|
|
mov ecx, CSR_DMACTL
|
|
; call [ebx + device.read_csr]
|
|
mov eax, 0xc00
|
|
call [ebx + device.write_csr]
|
|
|
|
mov [ebx + device.dxsuflo],1
|
|
mov [ebx + device.ltint],1
|
|
.L11:
|
|
|
|
; 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
|
|
|
|
mov [ebx + device.options], PORT_ASEL
|
|
mov [ebx + device.init_block.mode], MODE_RXD + MODE_TXD ; disable receive and transmit
|
|
mov [ebx + device.init_block.tlen_rlen], (TX_RING_LEN_BITS or RX_RING_LEN_BITS)
|
|
|
|
mov dword[ebx + device.init_block.filter], 0
|
|
mov dword[ebx + device.init_block.filter+4], 0
|
|
|
|
align 4
|
|
reset:
|
|
|
|
; Stop link check timer if it was already running
|
|
cmp [ebx + device.link_timer], 0
|
|
je @f
|
|
invoke CancelTimerHS, [ebx + device.link_timer]
|
|
@@:
|
|
|
|
; 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
|
|
@@:
|
|
|
|
mov edx, [ebx + device.io_addr]
|
|
call [ebx + device.sw_reset]
|
|
|
|
; Switch pcnet32 to 32bit mode
|
|
mov ecx, BCR_SSTYLE
|
|
mov eax, 2
|
|
call [ebx + device.write_bcr]
|
|
|
|
; set/reset autoselect bit
|
|
mov ecx, BCR_MISCCFG
|
|
call [ebx + device.read_bcr]
|
|
and eax, not 2
|
|
test [ebx + device.options], PORT_ASEL
|
|
jz @f
|
|
or eax, 2
|
|
@@:
|
|
call [ebx + device.write_bcr]
|
|
|
|
; Handle full duplex setting
|
|
cmp byte [ebx + device.full_duplex], 0
|
|
je .duplex_ok
|
|
mov ecx, BCR_DUPLEX
|
|
call [ebx + device.read_bcr]
|
|
and eax, not 3
|
|
test [ebx + device.options], PORT_FD
|
|
jz @f
|
|
or eax, 1
|
|
cmp [ebx + device.options], PORT_FD or PORT_AUI
|
|
jne .set_duplex
|
|
or eax, 2
|
|
jmp .set_duplex
|
|
@@:
|
|
test [ebx + device.options], PORT_ASEL
|
|
jz .set_duplex
|
|
cmp [ebx + device.chip_version], 0x2627
|
|
jne .set_duplex
|
|
or eax, 3
|
|
.set_duplex:
|
|
mov ecx, BCR_DUPLEX
|
|
call [ebx + device.write_bcr]
|
|
.duplex_ok:
|
|
|
|
; set/reset GPSI bit in test register
|
|
mov ecx, 124
|
|
call [ebx + device.read_csr]
|
|
mov ecx, [ebx + device.options]
|
|
and ecx, PORT_PORTSEL
|
|
cmp ecx, PORT_GPSI
|
|
jne @f
|
|
or eax, 0x10
|
|
@@:
|
|
call [ebx + device.write_csr]
|
|
cmp [ebx + device.mii], 0
|
|
je .L6
|
|
test [ebx + device.options], PORT_ASEL
|
|
jnz .L6
|
|
mov ecx, BCR_MIICTL
|
|
call [ebx + device.read_bcr]
|
|
and eax, not 0x38
|
|
test [ebx + device.options], PORT_FD
|
|
jz @f
|
|
or eax, 0x10
|
|
@@:
|
|
test [ebx + device.options], PORT_100
|
|
jz @f
|
|
or eax, 0x08
|
|
@@:
|
|
call [ebx + device.write_bcr]
|
|
jmp .L9
|
|
.L6:
|
|
test [ebx + device.options], PORT_ASEL
|
|
jz .L9
|
|
mov ecx, BCR_MIICTL
|
|
DEBUGF 1,"ASEL, enable auto-negotiation\n"
|
|
call [ebx + device.read_bcr]
|
|
and eax, not 0x98
|
|
or eax, 0x20
|
|
call [ebx + device.write_bcr]
|
|
.L9:
|
|
cmp [ebx + device.ltint], 0
|
|
je @f
|
|
mov ecx, 5
|
|
call [ebx + device.read_csr]
|
|
or eax, (1 shl 14)
|
|
call [ebx + device.write_csr]
|
|
@@:
|
|
mov eax, [ebx + device.options]
|
|
and eax, PORT_PORTSEL
|
|
shl eax, 7
|
|
mov [ebx + device.init_block.mode], ax
|
|
mov dword [ebx + device.init_block.filter], -1
|
|
mov dword [ebx + device.init_block.filter+4], -1
|
|
|
|
|
|
|
|
;-----------------------------
|
|
|
|
test [ebx + device.mii], 1
|
|
jz .no_mii
|
|
|
|
mov [ebx + device.phy], 0
|
|
|
|
.mii_loop:
|
|
mov ecx, MII_PHYSID1
|
|
call mdio_read
|
|
cmp ax, 0xffff
|
|
je .next
|
|
|
|
DEBUGF 1, "PHY ID1: 0x%x\n", ax
|
|
|
|
mov ecx, MII_PHYSID2
|
|
call mdio_read
|
|
cmp ax, 0xffff
|
|
je .next
|
|
|
|
DEBUGF 1, "PHY ID2: 0x%x\n", ax
|
|
|
|
jmp .got_phy
|
|
|
|
cmp [ebx + device.phy], 31
|
|
jne .next
|
|
mov ax, [ebx + device.chip_version]
|
|
inc ax
|
|
and ax, 0xfffe
|
|
cmp ax, 0x2624 ; 79c971 & 79c972 have phantom phy at id 31
|
|
je .got_phy
|
|
|
|
.next:
|
|
inc [ebx + device.phy]
|
|
cmp [ebx + device.phy], MAX_PHYS
|
|
jb .mii_loop
|
|
|
|
DEBUGF 2, "No PHY found!\n"
|
|
or eax, -1
|
|
ret
|
|
|
|
.got_phy:
|
|
DEBUGF 1, "Found PHY at 0x%x\n", [ebx + device.phy]:4
|
|
|
|
.no_mii:
|
|
|
|
;-----------------------------------------------
|
|
|
|
call read_mac
|
|
|
|
lea esi, [ebx + device.mac]
|
|
lea edi, [ebx + device.init_block.phys_addr]
|
|
movsd
|
|
movsw
|
|
|
|
call init_ring
|
|
test eax, eax
|
|
jnz .fail
|
|
|
|
mov edx, [ebx + device.io_addr] ; init ring destroys edx
|
|
|
|
lea eax, [ebx + device.init_block]
|
|
invoke GetPhysAddr
|
|
push eax
|
|
and eax, 0xffff
|
|
mov ecx, CSR_IAB0
|
|
call [ebx + device.write_csr]
|
|
pop eax
|
|
shr eax, 16
|
|
mov ecx, CSR_IAB1
|
|
call [ebx + device.write_csr]
|
|
|
|
mov ecx, CSR_TFEAT
|
|
mov eax, 0x0915 ; Auto TX PAD ?
|
|
call [ebx + device.write_csr]
|
|
|
|
; Set the interrupt mask
|
|
mov ecx, CSR_IMR
|
|
mov eax, IMR
|
|
call [ebx + device.write_csr]
|
|
|
|
; Initialise the device
|
|
xor ecx, ecx
|
|
mov eax, CSR_INIT
|
|
call [ebx + device.write_csr]
|
|
|
|
mov esi, 100
|
|
; xor ecx, ecx
|
|
@@:
|
|
call [ebx + device.read_csr]
|
|
test ax, CSR_IDONE
|
|
jnz @f
|
|
|
|
dec esi
|
|
jnz @r
|
|
DEBUGF 2,"Initialize timeout!\n"
|
|
@@:
|
|
|
|
; Start the device and enable interrupts
|
|
xor ecx, ecx
|
|
mov eax, CSR_START + CSR_INTEN
|
|
call [ebx + device.write_csr]
|
|
|
|
; Set the MTU
|
|
mov [ebx + device.mtu], 1514
|
|
|
|
mov [ebx + device.state], ETH_LINK_UNKNOWN
|
|
; Start media check timer
|
|
cmp [ebx + device.mii], 0
|
|
je @f
|
|
mov [ebx + device.state], ETH_LINK_DOWN
|
|
invoke TimerHS, 0, 50, check_media_mii, ebx
|
|
mov [ebx + device.link_timer], eax
|
|
@@:
|
|
|
|
DEBUGF 1,"reset complete\n"
|
|
xor eax, eax
|
|
.fail:
|
|
ret
|
|
|
|
|
|
align 4
|
|
init_ring:
|
|
|
|
DEBUGF 1,"init ring\n"
|
|
|
|
lea edi, [ebx + device.rx_ring]
|
|
mov eax, edi
|
|
invoke GetPhysAddr
|
|
mov [ebx + device.init_block.rx_ring_phys], eax
|
|
mov ecx, RX_RING_SIZE
|
|
.rx_init:
|
|
push ecx
|
|
invoke NetAlloc, PKT_BUF_SZ+NET_BUFF.data
|
|
pop ecx
|
|
test eax, eax
|
|
jz .out_of_mem
|
|
mov [edi + descriptor.virtual], eax
|
|
invoke GetPhysAddr
|
|
add eax, NET_BUFF.data
|
|
mov [edi + descriptor.base], eax
|
|
mov [edi + descriptor.length], - PKT_BUF_SZ
|
|
mov dword[edi + descriptor.msg_length], 0 ; also clears misc field
|
|
mov [edi + descriptor.status], RXSTAT_OWN
|
|
add edi, sizeof.descriptor
|
|
dec ecx
|
|
jnz .rx_init
|
|
|
|
lea edi, [ebx + device.tx_ring]
|
|
mov eax, edi
|
|
invoke GetPhysAddr
|
|
mov [ebx + device.init_block.tx_ring_phys], eax
|
|
mov ecx, TX_RING_SIZE
|
|
.tx_init:
|
|
mov [edi + descriptor.status], 0
|
|
add edi, sizeof.descriptor
|
|
dec ecx
|
|
jnz .tx_init
|
|
|
|
mov [ebx + device.init_block.tlen_rlen], (TX_RING_LEN_BITS or RX_RING_LEN_BITS)
|
|
|
|
mov [ebx + device.cur_tx], 0
|
|
mov [ebx + device.last_tx], 0
|
|
mov [ebx + device.cur_rx], 0
|
|
|
|
xor eax, eax
|
|
ret
|
|
|
|
.out_of_mem:
|
|
DEBUGF 2,"Out of memory!\n"
|
|
|
|
or eax, -1
|
|
ret
|
|
|
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; ;;
|
|
;; Transmit ;;
|
|
;; ;;
|
|
;; In: pointer to device structure in ebx ;;
|
|
;; ;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
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
|
|
|
|
; check descriptor
|
|
lea edi, [ebx + device.tx_ring]
|
|
mov ecx, [ebx + device.cur_tx]
|
|
shl ecx, 4
|
|
add edi, ecx
|
|
|
|
test [edi + descriptor.status], TXCTL_OWN
|
|
jnz .overrun
|
|
; descriptor is free, use it
|
|
mov [edi + descriptor.virtual], esi
|
|
mov eax, esi
|
|
add eax, [eax + NET_BUFF.offset]
|
|
invoke GetPhysAddr
|
|
mov [edi + descriptor.base], eax
|
|
; set length
|
|
mov eax, [esi + NET_BUFF.length]
|
|
neg eax
|
|
mov [edi + descriptor.length], ax
|
|
; put to transfer queue
|
|
mov [edi + descriptor.status], TXCTL_OWN + TXCTL_STP + TXCTL_ENP
|
|
|
|
; trigger an immediate send
|
|
mov edx, [ebx + device.io_addr]
|
|
xor ecx, ecx ; CSR0
|
|
call [ebx + device.read_csr]
|
|
or eax, CSR_TX
|
|
call [ebx + device.write_csr]
|
|
|
|
; get next descriptor
|
|
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 4
|
|
int_handler:
|
|
|
|
push ebx esi edi
|
|
|
|
DEBUGF 1,"INT\n"
|
|
|
|
; find pointer of device wich made IRQ occur
|
|
|
|
mov ecx, [devices]
|
|
test ecx, ecx
|
|
jz .nothing
|
|
mov esi, device_list
|
|
.nextdevice:
|
|
mov ebx, [esi]
|
|
mov edx, [ebx + device.io_addr]
|
|
push ecx
|
|
xor ecx, ecx ; CSR0
|
|
call [ebx + device.read_csr] ; get IRQ reason
|
|
call [ebx + device.write_csr] ; write it back to ACK
|
|
pop ecx
|
|
test ax, CSR_RINT or CSR_TINT
|
|
jnz .got_it
|
|
.continue:
|
|
add esi, 4
|
|
dec ecx
|
|
jnz .nextdevice
|
|
.nothing:
|
|
pop edi esi ebx
|
|
xor eax, eax
|
|
|
|
ret
|
|
|
|
.got_it:
|
|
DEBUGF 1,"Device: %x status: %x\n", ebx, eax:4
|
|
|
|
push ax
|
|
test ax, CSR_RINT
|
|
jz .not_receive
|
|
|
|
push ebx
|
|
.rx_loop:
|
|
pop ebx
|
|
push ebx
|
|
mov eax, [ebx + device.cur_rx]
|
|
shl eax, 4
|
|
lea edi, [ebx + device.rx_ring]
|
|
add edi, eax ; edi now points to current rx ring entry
|
|
|
|
mov ax, [edi + descriptor.status]
|
|
DEBUGF 1,"RX packet status: %x\n", eax:4
|
|
|
|
test ax, RXSTAT_OWN ; If this bit is set, the controller OWN's the packet, if not, we do
|
|
jnz .rx_done
|
|
; Both Start of packet and End of packet bits should be set, we dont support multi frame packets
|
|
test ax, RXSTAT_ENP
|
|
jz .rx_drop
|
|
test ax, RXSTAT_STP
|
|
jz .rx_drop
|
|
|
|
movzx ecx, [edi + descriptor.msg_length] ; get packet length in ecx
|
|
sub ecx, 4 ; We dont need the CRC
|
|
DEBUGF 1,"Got %u bytes\n", ecx
|
|
|
|
; Set pointers for ETH_input
|
|
push .rx_loop ; return address
|
|
mov eax, [edi + descriptor.virtual]
|
|
push eax ; packet address
|
|
mov [eax + NET_BUFF.length], ecx
|
|
mov [eax + NET_BUFF.device], ebx
|
|
mov [eax + NET_BUFF.offset], NET_BUFF.data
|
|
|
|
; Update stats
|
|
add dword[ebx + device.bytes_rx], ecx
|
|
adc dword[ebx + device.bytes_rx + 4], 0
|
|
inc [ebx + device.packets_rx]
|
|
|
|
; now allocate a new buffer
|
|
invoke NetAlloc, PKT_BUF_SZ+NET_BUFF.data ; Allocate a buffer for the next packet
|
|
test eax, eax
|
|
jz .rx_overrun
|
|
mov [edi + descriptor.virtual], eax ; set virtual address
|
|
invoke GetPhysAddr
|
|
add eax, NET_BUFF.data
|
|
mov [edi + descriptor.base], eax ; and physical address
|
|
mov [edi + descriptor.status], RXSTAT_OWN ; give it back to PCnet controller
|
|
|
|
inc [ebx + device.cur_rx] ; set next receive descriptor
|
|
and [ebx + device.cur_rx], RX_RING_SIZE - 1
|
|
|
|
jmp [EthInput]
|
|
|
|
.rx_overrun:
|
|
add esp, 4+4
|
|
DEBUGF 2,"RX FIFO overrun\n"
|
|
inc [ebx + device.packets_rx_ovr]
|
|
jmp .rx_next
|
|
|
|
.rx_drop:
|
|
DEBUGF 2,"Dropping incoming packet\n"
|
|
inc [ebx + device.packets_rx_drop]
|
|
|
|
.rx_next:
|
|
mov [edi + descriptor.status], RXSTAT_OWN ; give it back to PCnet controller
|
|
|
|
inc [ebx + device.cur_rx] ; set next receive descriptor
|
|
and [ebx + device.cur_rx], RX_RING_SIZE - 1
|
|
jmp .rx_loop
|
|
|
|
.rx_done:
|
|
pop ebx
|
|
|
|
.not_receive:
|
|
pop ax
|
|
|
|
test ax, CSR_TINT
|
|
jz .not_transmit
|
|
|
|
.tx_loop:
|
|
lea edi, [ebx + device.tx_ring]
|
|
mov eax, [ebx + device.last_tx]
|
|
shl eax, 4
|
|
add edi, eax
|
|
|
|
test [edi + descriptor.status], TXCTL_OWN
|
|
jnz .not_transmit
|
|
|
|
mov eax, [edi + descriptor.virtual]
|
|
test eax, eax
|
|
jz .not_transmit
|
|
|
|
mov [edi + descriptor.virtual], 0
|
|
|
|
DEBUGF 1,"Removing packet %x from memory\n", eax
|
|
|
|
invoke NetFree, eax
|
|
|
|
inc [ebx + device.last_tx]
|
|
and [ebx + device.last_tx], TX_RING_SIZE - 1
|
|
jmp .tx_loop
|
|
|
|
.not_transmit:
|
|
pop edi esi ebx
|
|
xor eax, eax
|
|
inc eax
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; ;;
|
|
;; Write MAC address ;;
|
|
;; ;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
align 4
|
|
write_mac: ; in: mac pushed onto stack (as 3 words)
|
|
|
|
DEBUGF 1,"Writing MAC: %x-%x-%x-%x-%x-%x\n",\
|
|
[esp+0]:2,[esp+1]:2,[esp+2]:2,[esp+3]:2,[esp+4]:2,[esp+5]:2
|
|
|
|
mov edx, [ebx + device.io_addr]
|
|
add dx, 2
|
|
xor eax, eax
|
|
|
|
mov ecx, CSR_PAR0
|
|
@@:
|
|
pop ax
|
|
call [ebx + device.write_csr]
|
|
inc ecx
|
|
cmp ecx, CSR_PAR2
|
|
jb @r
|
|
|
|
; Notice this procedure does not ret, but continues to read_mac instead.
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;
|
|
;; ;;
|
|
;; Read MAC address ;;
|
|
;; ;;
|
|
;;;;;;;;;;;;;;;;;;;;;;
|
|
align 4
|
|
read_mac:
|
|
DEBUGF 1,"Reading MAC\n"
|
|
|
|
mov edx, [ebx + device.io_addr]
|
|
lea edi, [ebx + device.mac]
|
|
in ax, dx
|
|
stosw
|
|
|
|
inc dx
|
|
inc dx
|
|
in ax, dx
|
|
stosw
|
|
|
|
inc dx
|
|
inc dx
|
|
in ax, dx
|
|
stosw
|
|
|
|
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
|
|
|
|
align 4
|
|
switch_to_wio:
|
|
|
|
DEBUGF 1,"Switching to 16-bit mode\n"
|
|
|
|
mov [ebx + device.read_csr], wio_read_csr
|
|
mov [ebx + device.write_csr], wio_write_csr
|
|
mov [ebx + device.read_bcr], wio_read_bcr
|
|
mov [ebx + device.write_bcr], wio_write_bcr
|
|
mov [ebx + device.read_rap], wio_read_rap
|
|
mov [ebx + device.write_rap], wio_write_rap
|
|
mov [ebx + device.sw_reset], wio_reset
|
|
|
|
ret
|
|
|
|
align 4
|
|
switch_to_dwio:
|
|
|
|
DEBUGF 1,"Switching to 32-bit mode\n"
|
|
|
|
mov [ebx + device.read_csr], dwio_read_csr
|
|
mov [ebx + device.write_csr], dwio_write_csr
|
|
mov [ebx + device.read_bcr], dwio_read_bcr
|
|
mov [ebx + device.write_bcr], dwio_write_bcr
|
|
mov [ebx + device.read_rap], dwio_read_rap
|
|
mov [ebx + device.write_rap], dwio_write_rap
|
|
mov [ebx + device.sw_reset], dwio_reset
|
|
|
|
ret
|
|
|
|
|
|
; ecx - index
|
|
; return:
|
|
; eax - data
|
|
align 4
|
|
wio_read_csr:
|
|
|
|
add edx, WIO_RAP
|
|
mov ax, cx
|
|
out dx, ax
|
|
add edx, WIO_RDP - WIO_RAP
|
|
in ax, dx
|
|
and eax, 0xffff
|
|
sub edx, WIO_RDP
|
|
|
|
ret
|
|
|
|
|
|
; eax - data
|
|
; ecx - index
|
|
align 4
|
|
wio_write_csr:
|
|
|
|
add edx, WIO_RAP
|
|
xchg eax, ecx
|
|
out dx, ax
|
|
xchg eax, ecx
|
|
add edx, WIO_RDP - WIO_RAP
|
|
out dx, ax
|
|
sub edx, WIO_RDP
|
|
|
|
ret
|
|
|
|
|
|
; ecx - index
|
|
; return:
|
|
; eax - data
|
|
align 4
|
|
wio_read_bcr:
|
|
|
|
add edx, WIO_RAP
|
|
mov ax, cx
|
|
out dx, ax
|
|
add edx, WIO_BDP - WIO_RAP
|
|
in ax, dx
|
|
and eax, 0xffff
|
|
sub edx, WIO_BDP
|
|
|
|
ret
|
|
|
|
|
|
; eax - data
|
|
; ecx - index
|
|
align 4
|
|
wio_write_bcr:
|
|
|
|
add edx, WIO_RAP
|
|
xchg eax, ecx
|
|
out dx, ax
|
|
xchg eax, ecx
|
|
add edx, WIO_BDP - WIO_RAP
|
|
out dx, ax
|
|
sub edx, WIO_BDP
|
|
|
|
ret
|
|
|
|
align 4
|
|
wio_read_rap:
|
|
|
|
add edx, WIO_RAP
|
|
in ax, dx
|
|
and eax, 0xffff
|
|
sub edx, WIO_RAP
|
|
|
|
ret
|
|
|
|
; eax - val
|
|
align 4
|
|
wio_write_rap:
|
|
|
|
add edx, WIO_RAP
|
|
out dx, ax
|
|
sub edx, WIO_RAP
|
|
|
|
ret
|
|
|
|
align 4
|
|
wio_reset:
|
|
|
|
push eax
|
|
add edx, WIO_RESET
|
|
in ax, dx
|
|
pop eax
|
|
sub edx, WIO_RESET
|
|
|
|
ret
|
|
|
|
|
|
|
|
; ecx - index
|
|
; return:
|
|
; eax - data
|
|
align 4
|
|
dwio_read_csr:
|
|
|
|
add edx, DWIO_RAP
|
|
mov eax, ecx
|
|
out dx, eax
|
|
add edx, DWIO_RDP - DWIO_RAP
|
|
in eax, dx
|
|
and eax, 0xffff
|
|
sub edx, DWIO_RDP
|
|
|
|
ret
|
|
|
|
|
|
; ecx - index
|
|
; eax - data
|
|
align 4
|
|
dwio_write_csr:
|
|
|
|
add edx, DWIO_RAP
|
|
xchg eax, ecx
|
|
out dx, eax
|
|
add edx, DWIO_RDP - DWIO_RAP
|
|
xchg eax, ecx
|
|
out dx, eax
|
|
sub edx, DWIO_RDP
|
|
|
|
ret
|
|
|
|
; ecx - index
|
|
; return:
|
|
; eax - data
|
|
align 4
|
|
dwio_read_bcr:
|
|
|
|
add edx, DWIO_RAP
|
|
mov eax, ecx
|
|
out dx, eax
|
|
add edx, DWIO_BDP - DWIO_RAP
|
|
in eax, dx
|
|
and eax, 0xffff
|
|
sub edx, DWIO_BDP
|
|
|
|
ret
|
|
|
|
|
|
; ecx - index
|
|
; eax - data
|
|
align 4
|
|
dwio_write_bcr:
|
|
|
|
add edx, DWIO_RAP
|
|
xchg eax, ecx
|
|
out dx, eax
|
|
add edx, DWIO_BDP - DWIO_RAP
|
|
xchg eax, ecx
|
|
out dx, eax
|
|
sub edx, DWIO_BDP
|
|
|
|
ret
|
|
|
|
align 4
|
|
dwio_read_rap:
|
|
|
|
add edx, DWIO_RAP
|
|
in eax, dx
|
|
and eax, 0xffff
|
|
sub edx, DWIO_RAP
|
|
|
|
ret
|
|
|
|
|
|
; eax - val
|
|
align 4
|
|
dwio_write_rap:
|
|
|
|
add edx, DWIO_RAP
|
|
out dx, eax
|
|
sub edx, DWIO_RAP
|
|
|
|
ret
|
|
|
|
align 4
|
|
dwio_reset:
|
|
|
|
push eax
|
|
add edx, DWIO_RESET
|
|
in eax, dx
|
|
pop eax
|
|
sub edx, DWIO_RESET
|
|
|
|
ret
|
|
|
|
|
|
align 4
|
|
mdio_read:
|
|
|
|
and ecx, 0x1f
|
|
mov ax, [ebx + device.phy]
|
|
and ax, 0x1f
|
|
shl ax, 5
|
|
or ax, cx
|
|
|
|
mov ecx, BCR_MIIADDR
|
|
call [ebx + device.write_bcr]
|
|
|
|
mov ecx, BCR_MIIDATA
|
|
call [ebx + device.read_bcr]
|
|
|
|
ret
|
|
|
|
|
|
align 4
|
|
mdio_write:
|
|
|
|
push eax
|
|
and ecx, 0x1f
|
|
mov ax, [ebx + device.phy]
|
|
and ax, 0x1f
|
|
shl ax, 5
|
|
or ax, cx
|
|
|
|
mov ecx, BCR_MIIADDR
|
|
call [ebx + device.write_bcr]
|
|
|
|
pop eax
|
|
mov ecx, BCR_MIIDATA
|
|
call [ebx + device.write_bcr]
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
proc check_media_mii stdcall dev:dword
|
|
|
|
spin_lock_irqsave
|
|
|
|
mov ebx, [dev]
|
|
mov edx, [ebx + device.io_addr]
|
|
|
|
mov ecx, MII_BMSR
|
|
call mdio_read
|
|
|
|
mov ecx, MII_BMSR
|
|
call mdio_read
|
|
|
|
mov ecx, eax
|
|
and eax, BMSR_LSTATUS
|
|
shr eax, 2
|
|
cmp eax, [ebx + device.state]
|
|
jne .changed
|
|
|
|
spin_unlock_irqrestore
|
|
ret
|
|
|
|
.changed:
|
|
test eax, eax
|
|
jz .update
|
|
|
|
test ecx, BMSR_ANEGCOMPLETE
|
|
jz .update
|
|
|
|
mov ecx, MII_ADVERTISE
|
|
call mdio_read
|
|
mov esi, eax
|
|
|
|
mov ecx, MII_LPA
|
|
call mdio_read
|
|
and eax, esi
|
|
|
|
test eax, LPA_100FULL
|
|
jz @f
|
|
mov eax, ETH_LINK_SPEED_100M or ETH_LINK_FULL_DUPLEX
|
|
jmp .update
|
|
@@:
|
|
|
|
test eax, LPA_100HALF
|
|
jz @f
|
|
mov eax, ETH_LINK_SPEED_100M
|
|
jmp .update
|
|
@@:
|
|
|
|
test eax, LPA_10FULL
|
|
jz @f
|
|
mov eax, ETH_LINK_SPEED_10M or ETH_LINK_FULL_DUPLEX
|
|
jmp .update
|
|
@@:
|
|
|
|
test eax, LPA_10HALF
|
|
jz @f
|
|
mov eax, ETH_LINK_SPEED_10M
|
|
jmp .update
|
|
@@:
|
|
|
|
mov eax, ETH_LINK_UNKNOWN
|
|
|
|
.update:
|
|
mov [ebx + device.state], eax
|
|
invoke NetLinkChanged
|
|
|
|
|
|
spin_unlock_irqrestore
|
|
ret
|
|
|
|
|
|
endp
|
|
|
|
|
|
; End of code
|
|
|
|
|
|
data fixups
|
|
end data
|
|
|
|
include '../peimport.inc'
|
|
|
|
my_service db 'PCNET32',0 ; max 16 chars include zero
|
|
|
|
device_l2 db "PCnet/PCI 79C970",0
|
|
device_l4 db "PCnet/PCI II 79C970A",0
|
|
device_l5 db "PCnet/FAST 79C971",0
|
|
device_l6 db "PCnet/FAST+ 79C972",0
|
|
device_l7 db "PCnet/FAST III 79C973",0
|
|
device_l8 db "PCnet/Home 79C978",0
|
|
device_l9 db "PCnet/FAST III 79C975",0
|
|
|
|
options_mapping:
|
|
dd PORT_ASEL ; 0 Auto-select
|
|
dd PORT_AUI ; 1 BNC/AUI
|
|
dd PORT_AUI ; 2 AUI/BNC
|
|
dd PORT_ASEL ; 3 not supported
|
|
dd PORT_10BT or PORT_FD ; 4 10baseT-FD
|
|
dd PORT_ASEL ; 5 not supported
|
|
dd PORT_ASEL ; 6 not supported
|
|
dd PORT_ASEL ; 7 not supported
|
|
dd PORT_ASEL ; 8 not supported
|
|
dd PORT_MII ; 9 MII 10baseT
|
|
dd PORT_MII or PORT_FD ; 10 MII 10baseT-FD
|
|
dd PORT_MII ; 11 MII (autosel)
|
|
dd PORT_10BT ; 12 10BaseT
|
|
dd PORT_MII or PORT_100 ; 13 MII 100BaseTx
|
|
dd PORT_MII or PORT_100 or PORT_FD ; 14 MII 100BaseTx-FD
|
|
dd PORT_ASEL ; 15 not supported
|
|
|
|
include_debug_strings ; All data wich FDO uses will be included here
|
|
|
|
|
|
align 4
|
|
devices dd 0
|
|
device_list rd MAX_DEVICES ; This list contains all pointers to device structures the driver is handling
|