forked from KolibriOS/kolibrios
Andrew Dent
c7231e7d53
- Corrections for en_US language. - Some whitespace sanitation. git-svn-id: svn://kolibrios.org@10065 a494cfbc-eb01-0410-851d-a64ba20cac60
1267 lines
41 KiB
NASM
1267 lines
41 KiB
NASM
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; ;;
|
|
;; Copyright (C) KolibriOS team 2004-2024. All rights reserved. ;;
|
|
;; Distributed under terms of the GNU General Public License ;;
|
|
;; ;;
|
|
;; Ethernet driver for KolibriOS ;;
|
|
;; This is an adaptation of MenuetOS driver with minimal changes. ;;
|
|
;; Changes were made by CleverMouse. Original copyright follows. ;;
|
|
;; ;;
|
|
;; This driver is based on the SIS900 driver from ;;
|
|
;; the etherboot 5.0.6 project. The copyright statement is ;;
|
|
;; ;;
|
|
;; GNU GENERAL PUBLIC LICENSE ;;
|
|
;; Version 2, June 1991 ;;
|
|
;; ;;
|
|
;; remaining parts Copyright 2004 Jason Delozier, ;;
|
|
;; cordata51@hotmail.com ;;
|
|
;; ;;
|
|
;; See file COPYING for details ;;
|
|
;; ;;
|
|
;; Updates: ;;
|
|
;; Revision Look up table and SIS635 Mac Address by Jarek Pelczar ;;
|
|
;; ;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
format PE DLL native
|
|
entry START
|
|
|
|
CURRENT_API = 0x0200
|
|
COMPATIBLE_API = 0x0100
|
|
API_VERSION = (COMPATIBLE_API shl 16) + CURRENT_API
|
|
|
|
NUM_RX_DESC = 4 ; Number of RX descriptors
|
|
NUM_TX_DESC = 4 ; Number of TX descriptors
|
|
RX_BUFF_SZ = 1520 ; Buffer size for each Rx buffer
|
|
|
|
MAX_DEVICES = 16
|
|
|
|
__DEBUG__ = 1
|
|
__DEBUG_LEVEL__ = 2 ; 1 = verbose, 2 = errors only
|
|
|
|
DSIZE = 0x00000fff
|
|
CRC_SIZE = 4
|
|
RFADDR_shift = 16
|
|
|
|
; If you are having problems sending/receiving packet try changing the
|
|
; Max DMA Burst, Possible settings are as follows:
|
|
;
|
|
; 0x00000000 = 512 bytes
|
|
; 0x00100000 = 4 bytes
|
|
; 0x00200000 = 8 bytes
|
|
; 0x00300000 = 16 bytes
|
|
; 0x00400000 = 32 bytes
|
|
; 0x00500000 = 64 bytes
|
|
; 0x00600000 = 128 bytes
|
|
; 0x00700000 = 256 bytes
|
|
|
|
RX_DMA = 0x00600000
|
|
TX_DMA = 0x00600000
|
|
|
|
;-------------------------------------------------------------------------------------------------
|
|
; Symbolic offsets to registers.
|
|
cr = 0x0 ; Command Register
|
|
cfg = 0x4 ; Configuration Register
|
|
mear = 0x8 ; EEPROM Access Register
|
|
ptscr = 0xc ; PCI Test Control Register
|
|
isr = 0x10 ; Interrupt Status Register
|
|
imr = 0x14 ; Interrupt Mask Register
|
|
ier = 0x18 ; Interrupt Enable Register
|
|
epar = 0x18 ; Enhanced PHY Access Register
|
|
txdp = 0x20 ; Transmit Descriptor Pointer Register
|
|
txcfg = 0x24 ; Transmit Configuration Register
|
|
rxdp = 0x30 ; Receive Descriptor Pointer Register
|
|
rxcfg = 0x34 ; Receive Configuration Register
|
|
flctrl = 0x38 ; Flow Control Register
|
|
rxlen = 0x3c ; Receive Packet Length Register
|
|
rfcr = 0x48 ; Receive Filter Control Register
|
|
rfdr = 0x4C ; Receive Filter Data Register
|
|
pmctrl = 0xB0 ; Power Management Control Register
|
|
pmer = 0xB4 ; Power Management Wake-up Event Register
|
|
|
|
; Command Register Bits
|
|
RELOAD = 0x00000400
|
|
ACCESSMODE = 0x00000200
|
|
RESET = 0x00000100
|
|
SWI = 0x00000080
|
|
RxRESET = 0x00000020
|
|
TxRESET = 0x00000010
|
|
RxDIS = 0x00000008
|
|
RxENA = 0x00000004
|
|
TxDIS = 0x00000002
|
|
TxENA = 0x00000001
|
|
|
|
; Configuration Register Bits
|
|
DESCRFMT = 0x00000100 ; 7016 specific
|
|
REQALG = 0x00000080
|
|
SB = 0x00000040
|
|
POW = 0x00000020
|
|
EXD = 0x00000010
|
|
PESEL = 0x00000008
|
|
LPM = 0x00000004
|
|
BEM = 0x00000001
|
|
RND_CNT = 0x00000400
|
|
FAIR_BACKOFF = 0x00000200
|
|
EDB_MASTER_EN = 0x00002000
|
|
|
|
; Eeprom Access Reigster Bits
|
|
MDC = 0x00000040
|
|
MDDIR = 0x00000020
|
|
MDIO = 0x00000010 ; 7016 specific
|
|
EECS = 0x00000008
|
|
EECLK = 0x00000004
|
|
EEDO = 0x00000002
|
|
EEDI = 0x00000001
|
|
|
|
; TX Configuration Register Bits
|
|
ATP = 0x10000000 ; Automatic Transmit Padding
|
|
MLB = 0x20000000 ; Mac Loopback Enable
|
|
HBI = 0x40000000 ; HeartBeat Ignore (Req for full-dup)
|
|
CSI = 0x80000000 ; CarrierSenseIgnore (Req for full-du
|
|
|
|
; RX Configuration Register Bits
|
|
AJAB = 0x08000000 ;
|
|
ATX = 0x10000000 ; Accept Transmit Packets
|
|
ARP = 0x40000000 ; accept runt packets (<64bytes)
|
|
AEP = 0x80000000 ; accept error packets
|
|
|
|
; Interrupt Register Bits
|
|
WKEVT = 0x10000000
|
|
TxPAUSEEND = 0x08000000
|
|
TxPAUSE = 0x04000000
|
|
TxRCMP = 0x02000000 ; Transmit Reset Complete
|
|
RxRCMP = 0x01000000 ; Receive Reset Complete
|
|
DPERR = 0x00800000
|
|
SSERR = 0x00400000
|
|
RMABT = 0x00200000
|
|
RTABT = 0x00100000
|
|
RxSOVR = 0x00010000
|
|
HIBERR = 0x00008000
|
|
SWINT = 0x00001000
|
|
MIBINT = 0x00000800
|
|
TxURN = 0x00000400
|
|
TxIDLE = 0x00000200
|
|
TxERR = 0x00000100
|
|
TxDESC = 0x00000080
|
|
TxOK = 0x00000040
|
|
RxORN = 0x00000020
|
|
RxIDLE = 0x00000010
|
|
RxEARLY = 0x00000008
|
|
RxERR = 0x00000004
|
|
RxDESC = 0x00000002
|
|
RxOK = 0x00000001
|
|
|
|
; Interrupt Enable Register Bits
|
|
IE = RxOK + TxOK
|
|
|
|
; Revision ID
|
|
SIS900B_900_REV = 0x03
|
|
SIS630A_900_REV = 0x80
|
|
SIS630E_900_REV = 0x81
|
|
SIS630S_900_REV = 0x82
|
|
SIS630EA1_900_REV = 0x83
|
|
SIS630ET_900_REV = 0x84
|
|
SIS635A_900_REV = 0x90
|
|
SIS900_960_REV = 0x91
|
|
|
|
; Receive Filter Control Register Bits
|
|
RFEN = 0x80000000 ; enable
|
|
RFAAB = 0x40000000 ; accept all broadcasts
|
|
RFAAM = 0x20000000 ; accept all multicasts
|
|
RFAAP = 0x10000000 ; accept all packets
|
|
|
|
; Reveive Filter Data Mask
|
|
RFDAT = 0x0000FFFF
|
|
|
|
; Eeprom Address
|
|
EEPROMSignature = 0x00
|
|
EEPROMVendorID = 0x02
|
|
EEPROMDeviceID = 0x03
|
|
EEPROMMACAddr = 0x08
|
|
EEPROMChecksum = 0x0b
|
|
|
|
; The EEPROM commands include the alway-set leading bit.
|
|
EEread = 0x0180
|
|
EEwrite = 0x0140
|
|
EEerase = 0x01C0
|
|
EEwriteEnable = 0x0130
|
|
EEwriteDisable = 0x0100
|
|
EEeraseAll = 0x0120
|
|
EEwriteAll = 0x0110
|
|
EEaddrMask = 0x013F
|
|
EEcmdShift = 16
|
|
|
|
; For SiS962 or SiS963, request the eeprom software access
|
|
EEREQ = 0x00000400
|
|
EEDONE = 0x00000200
|
|
EEGNT = 0x00000100
|
|
|
|
section '.flat' readable writable executable
|
|
|
|
include '../proc32.inc'
|
|
include '../struct.inc'
|
|
include '../macros.inc'
|
|
include '../fdo.inc'
|
|
include '../netdrv.inc'
|
|
|
|
|
|
struct device ETH_DEVICE
|
|
|
|
io_addr dd ?
|
|
pci_bus dd ?
|
|
pci_dev dd ?
|
|
irq_line db ?
|
|
cur_rx db ?
|
|
cur_tx db ?
|
|
last_tx db ?
|
|
pci_revision db ?
|
|
table_entries db ?
|
|
|
|
rb 0x100 - ($ and 0xff) ; align 256
|
|
txd rd (4 * NUM_TX_DESC)
|
|
rxd rd (4 * NUM_RX_DESC)
|
|
|
|
ends
|
|
|
|
macro ee_delay {
|
|
push eax
|
|
in eax, dx
|
|
in eax, dx
|
|
in eax, dx
|
|
in eax, dx
|
|
in eax, dx
|
|
in eax, dx
|
|
in eax, dx
|
|
in eax, dx
|
|
in eax, dx
|
|
in eax, dx
|
|
pop eax
|
|
}
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; ;;
|
|
;; proc START ;;
|
|
;; ;;
|
|
;; (standard driver proc) ;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
; Driver entry point - register our service when the driver is loading.
|
|
; TODO: add needed operations when unloading
|
|
|
|
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) ;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
; Service procedure for the driver - handle all I/O requests for the driver.
|
|
; Currently handled requests are: SRV_GETVERSION = 0 and SRV_HOOK = 1.
|
|
service_proc:
|
|
; 1. Get parameter from the stack: [esp+4] is the first parameter,
|
|
; pointer to IOCTL structure.
|
|
mov edx, [esp+4] ; edx -> IOCTL
|
|
; 2. Get request code and select a handler for the code.
|
|
mov eax, [edx + IOCTL.io_code]
|
|
test eax, eax ; check for SRV_GETVERSION
|
|
jnz @f
|
|
; 3. This is SRV_GETVERSION request, no input, 4 bytes output, API_VERSION.
|
|
; 3a. Output size must be at least 4 bytes.
|
|
cmp [edx + IOCTL.out_size], 4
|
|
jb .fail
|
|
; 3b. Write result to the output buffer.
|
|
mov eax, [edx + IOCTL.output]
|
|
mov [eax], dword API_VERSION
|
|
; 3c. Return success.
|
|
xor eax, eax
|
|
ret 4
|
|
@@:
|
|
dec eax ; check for SRV_HOOK
|
|
jnz .fail
|
|
; 4. This is SRV_HOOK request, input defines the device to hook, no output.
|
|
; 4a. The driver works only with PCI devices,
|
|
; so input must be at least 3 bytes long.
|
|
cmp [edx + IOCTL.inp_size], 3
|
|
jb .fail
|
|
; 4b. First byte of input is bus type, 1 stands for PCI.
|
|
mov eax, [edx + IOCTL.input]
|
|
cmp byte [eax], 1
|
|
jne .fail
|
|
; 4c. Second and third bytes of the input define the device: bus and dev.
|
|
; Word in bx holds both bytes.
|
|
mov bx, [eax+1]
|
|
; 4d. Check if the device was already hooked,
|
|
; scan through the list of known devices.
|
|
; 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 its device number
|
|
@@:
|
|
add esi, 4
|
|
loop .nextdevice
|
|
; 4e. This device doesn't have its own eth_device structure yet, let's create one
|
|
.firstdevice:
|
|
; 4f. Check that we have place for new device.
|
|
cmp [devices], MAX_DEVICES
|
|
jae .fail
|
|
; 4g. Allocate memory for device descriptor and receive+transmit buffers.
|
|
; 4h. Zero the structure.
|
|
allocate_and_clear ebx, sizeof.device, .fail
|
|
; 4i. Save PCI coordinates
|
|
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
|
|
; 4j. 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
|
|
|
|
; 4k. Now, it's time to find the base io address of the PCI device
|
|
; TODO: implement check if bus and dev exist on this machine
|
|
|
|
; 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
|
|
|
|
; 4m. Add new device to the list (required for int_handler).
|
|
mov eax, [devices]
|
|
mov [device_list+4*eax], ebx
|
|
inc [devices]
|
|
|
|
; 4m. Ok, the eth_device structure is ready, let's probe the device
|
|
call probe
|
|
test eax, eax
|
|
jnz .destroy
|
|
; 4n. If device was successfully initialized, register it for the kernel.
|
|
|
|
mov [ebx + device.type], NET_TYPE_ETH
|
|
invoke NetRegDev
|
|
|
|
cmp eax, -1
|
|
je .destroy
|
|
|
|
ret 4
|
|
|
|
; 5. If the device was already loaded, find the device number and return it in eax
|
|
|
|
.find_devicenum:
|
|
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
|
|
ret 4
|
|
|
|
; If an error occured, remove all allocated data and exit (returning -1 in eax)
|
|
|
|
.destroy:
|
|
dec [devices]
|
|
; todo: reset device into virgin state
|
|
|
|
.err:
|
|
invoke KernelFree, ebx
|
|
|
|
.fail:
|
|
xor eax, eax
|
|
ret 4
|
|
|
|
|
|
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
|
|
;; ;;
|
|
;; Actual Hardware dependent code starts here ;;
|
|
;; ;;
|
|
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
|
|
|
|
unload:
|
|
; TODO: (in this particular order)
|
|
;
|
|
; - Stop the device
|
|
; - Detach int handler
|
|
; - Remove device from local list
|
|
; - call unregister function in kernel
|
|
; - Remove all allocated structures and buffers the card used
|
|
|
|
or eax, -1
|
|
ret
|
|
|
|
;***************************************************************************
|
|
;
|
|
; probe
|
|
;
|
|
; checks the card and enables it
|
|
;
|
|
; TODO: probe mii transceivers
|
|
;
|
|
;***************************************************************************
|
|
align 4
|
|
probe:
|
|
DEBUGF 1, "Probe\n"
|
|
|
|
; wake up device
|
|
; TODO: check capabilities pointer instead of using hardcoded offset.
|
|
invoke PciWrite8, [ebx + device.pci_bus], [ebx + device.pci_dev], 0x40, 0
|
|
|
|
; 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
|
|
|
|
; Adjust PCI latency to be at least 64
|
|
invoke PciRead8, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.max_latency
|
|
cmp al, 64
|
|
jae @f
|
|
mov al, 64
|
|
invoke PciWrite8, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.max_latency, eax
|
|
@@:
|
|
|
|
; Get Card Revision
|
|
invoke PciRead8, [ebx + device.pci_bus], [ebx + device.pci_dev], 0x08
|
|
mov [ebx + device.pci_revision], al ; save the revision for later use
|
|
|
|
; Look up through the specific_table
|
|
mov esi, specific_table
|
|
.tableloop:
|
|
cmp dword[esi], 0 ; Check if we reached end of the list
|
|
je .notsupported
|
|
cmp al, [esi] ; Check if revision is OK
|
|
je .ok
|
|
add esi, 12 ; Advance to next entry
|
|
jmp .tableloop
|
|
.ok:
|
|
call dword[esi + 4] ; "get MAC" function
|
|
|
|
; Set table entries
|
|
mov [ebx + device.table_entries], 16
|
|
cmp [ebx + device.pci_revision], SIS635A_900_REV
|
|
jae @f
|
|
cmp [ebx + device.pci_revision], SIS900B_900_REV
|
|
je @f
|
|
mov [ebx + device.table_entries], 8
|
|
@@:
|
|
|
|
; TODO: Probe for mii transceiver
|
|
jmp reset
|
|
|
|
.notsupported:
|
|
DEBUGF 2, "Device not supported\n"
|
|
or eax, -1
|
|
|
|
ret
|
|
|
|
reset:
|
|
|
|
DEBUGF 1, "reset\n"
|
|
|
|
movzx eax, [ebx + device.irq_line]
|
|
invoke AttachIntHandler, eax, int_handler, ebx
|
|
test eax, eax
|
|
jnz @f
|
|
DEBUGF 2,"Could not attach int handler!\n"
|
|
or eax, -1
|
|
ret
|
|
@@:
|
|
|
|
;--------------------------------------------
|
|
; Disable Interrupts and reset Receive Filter
|
|
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], ier
|
|
xor eax, eax
|
|
out dx, eax
|
|
|
|
set_io [ebx + device.io_addr], imr
|
|
out dx, eax
|
|
|
|
set_io [ebx + device.io_addr], rfcr
|
|
out dx, eax
|
|
|
|
;-----------
|
|
; Reset Card
|
|
|
|
set_io [ebx + device.io_addr], cr
|
|
in eax, dx ; Get current Command Register
|
|
or eax, RESET + RxRESET + TxRESET ; set flags
|
|
out dx, eax ; Write new Command Register
|
|
|
|
;----------
|
|
; Wait loop
|
|
|
|
set_io [ebx + device.io_addr], isr
|
|
mov ecx, 1000
|
|
.loop:
|
|
dec ecx
|
|
jz .fail
|
|
in eax, dx ; read interrupt status
|
|
test eax, TxRCMP + RxRCMP
|
|
jz .loop
|
|
DEBUGF 1, "status=%x\n", eax
|
|
|
|
;------------------------------------------------------
|
|
; Set Configuration Register depending on Card Revision
|
|
|
|
set_io [ebx + device.io_addr], cfg
|
|
mov eax, PESEL ; Configuration Register Bit
|
|
cmp [ebx + device.pci_revision], SIS635A_900_REV
|
|
je .match
|
|
cmp [ebx + device.pci_revision], SIS900B_900_REV ; Check card revision
|
|
jne .done
|
|
.match: ; Revision match
|
|
or eax, RND_CNT ; Configuration Register Bit
|
|
.done:
|
|
out dx, eax
|
|
|
|
DEBUGF 1, "Initialising RX Filter\n"
|
|
|
|
; Get Receive Filter Control Register
|
|
set_io [ebx + device.io_addr], rfcr
|
|
in eax, dx
|
|
push eax
|
|
|
|
; disable packet filtering before setting filter
|
|
and eax, not RFEN
|
|
out dx, eax
|
|
|
|
; load MAC addr to filter data register
|
|
xor ecx, ecx
|
|
.macloop:
|
|
mov eax, ecx
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], rfcr
|
|
shl eax, 16 ; high word of eax tells card which mac byte to write
|
|
out dx, eax ;
|
|
set_io [ebx + device.io_addr], rfdr
|
|
mov ax, word [ebx + device.mac + ecx*2] ; Get Mac ID word
|
|
out dx, ax ; Send Mac ID
|
|
inc cl ; send next word
|
|
cmp cl, 3 ; more to send?
|
|
jne .macloop
|
|
|
|
; enable packet filtering
|
|
pop eax ; old register value
|
|
set_io [ebx + device.io_addr], rfcr
|
|
or eax, RFEN ; enable filtering
|
|
out dx, eax ; set register
|
|
|
|
DEBUGF 1, "Initialising TX Descriptors\n"
|
|
|
|
mov ecx, NUM_TX_DESC
|
|
lea esi, [ebx + device.txd]
|
|
.txdescloop:
|
|
lea eax, [esi + 16] ; next ptr
|
|
invoke GetPhysAddr
|
|
mov dword[esi], eax ; link to next desc
|
|
mov dword[esi + 4], 0 ; status field
|
|
mov dword[esi + 8], 0 ; ptr to buffer
|
|
add esi, 16
|
|
dec ecx
|
|
jnz .txdescloop
|
|
|
|
lea eax, [ebx + device.txd]
|
|
invoke GetPhysAddr
|
|
mov dword[esi - 16], eax ; correct last descriptor link ptr
|
|
|
|
set_io [ebx + device.io_addr], txdp ; TX Descriptor Pointer
|
|
; lea eax, [ebx + device.txd]
|
|
; invoke GetPhysAddr
|
|
out dx, eax
|
|
|
|
mov [ebx + device.cur_tx], 0 ; Set current tx descriptor to 0
|
|
mov [ebx + device.last_tx], 0
|
|
|
|
DEBUGF 1, "Initialising RX Descriptors\n"
|
|
|
|
mov ecx, NUM_RX_DESC
|
|
lea esi, [ebx + device.rxd]
|
|
.rxdescloop:
|
|
lea eax, [esi + 16] ; next ptr
|
|
invoke GetPhysAddr
|
|
mov dword [esi], eax
|
|
mov dword [esi + 4], RX_BUFF_SZ ; size
|
|
|
|
push ecx esi
|
|
invoke NetAlloc, RX_BUFF_SZ+NET_BUFF.data
|
|
pop esi ecx
|
|
test eax, eax
|
|
jz .fail
|
|
mov dword [esi + 12], eax ; address
|
|
invoke GetPhysAddr
|
|
add eax, NET_BUFF.data
|
|
mov dword [esi + 8], eax ; real address
|
|
add esi, 16
|
|
dec ecx
|
|
jnz .rxdescloop
|
|
|
|
lea eax, [ebx + device.rxd]
|
|
invoke GetPhysAddr
|
|
mov dword [esi - 16], eax ; correct last descriptor link ptr
|
|
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], rxdp
|
|
; lea eax, [ebx + device.rxd]
|
|
; invoke GetPhysAddr
|
|
out dx, eax
|
|
|
|
mov [ebx + device.cur_rx], 0 ; Set current rx descriptor to 0
|
|
|
|
DEBUGF 1, "setting RX mode\n"
|
|
|
|
xor cl, cl
|
|
.rxfilterloop:
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], rfcr ; Receive Filter Control Reg offset
|
|
mov eax, 4 ; determine table entry
|
|
add al, cl
|
|
shl eax, 16
|
|
out dx, eax ; tell card which entry to modify
|
|
|
|
set_io [ebx + device.io_addr], rfdr ; Receive Filter Control Reg offset
|
|
mov eax, 0xffff ; entry value
|
|
out dx, ax ; write value to table in card
|
|
|
|
inc cl ; next entry
|
|
cmp cl, [ebx + device.table_entries]
|
|
jb .rxfilterloop
|
|
|
|
set_io [ebx + device.io_addr], rfcr ; Receive Filter Control Register offset
|
|
mov eax, RFAAB + RFAAM + RFAAP + RFEN
|
|
out dx, eax
|
|
|
|
set_io [ebx + device.io_addr], rxcfg ; Receive Config Register offset
|
|
mov eax, ATX + RX_DMA + 2 ; 0x2 : RX Drain Threshold = 8*8=64 bytes
|
|
out dx, eax
|
|
|
|
DEBUGF 1, "setting TX mode\n"
|
|
|
|
set_io [ebx + device.io_addr], txcfg ; Transmit config Register offset
|
|
mov eax, ATP + HBI + CSI + TX_DMA + 0x120 ; TX Fill threshold = 0x100
|
|
; TX Drain Threshold = 0x20
|
|
out dx, eax
|
|
|
|
DEBUGF 1, "Enabling interrupts\n"
|
|
|
|
set_io [ebx + device.io_addr], imr
|
|
mov eax, IE ; Interrupt enable mask
|
|
out dx, eax
|
|
|
|
set_io [ebx + device.io_addr], cr
|
|
in eax, dx
|
|
or eax, RxENA ; Enable Receive
|
|
out dx, eax
|
|
|
|
set_io [ebx + device.io_addr], ier ; Interrupt enable
|
|
mov eax, 1
|
|
out dx, eax
|
|
|
|
mov [ebx + device.mtu], 1514
|
|
|
|
; Set link state to unknown
|
|
mov [ebx + device.state], ETH_LINK_UNKNOWN
|
|
|
|
xor eax, eax
|
|
ret
|
|
|
|
.fail:
|
|
DEBUGF 2, "Resetting device failed\n"
|
|
or eax, -1
|
|
|
|
ret
|
|
|
|
|
|
;***************************************************************************
|
|
;
|
|
; SIS960_get_mac_addr: - Get MAC address for SiS962 or SiS963 model
|
|
;
|
|
; SiS962 or SiS963 model, use EEPROM to store MAC address.
|
|
; EEPROM is shared by LAN and 1394.
|
|
; When access EEPROM, send EEREQ signal to hardware first, and wait for EEGNT.
|
|
; If EEGNT is ON, EEPROM is permitted to be accessed by LAN, otherwise is not.
|
|
; After MAC address is read from EEPROM, send
|
|
; EEDONE signal to refuse EEPROM access by LAN.
|
|
; The EEPROM map of SiS962 or SiS963 is different to SiS900.
|
|
; The signature field in SiS962 or SiS963 spec is meaningless.
|
|
;
|
|
; Return 0 is EAX = failure
|
|
;
|
|
;***************************************************************************
|
|
align 4
|
|
SIS960_get_mac_addr:
|
|
DEBUGF 1, "SIS960 - get mac:\n"
|
|
|
|
;-------------------------------
|
|
; Send Request for eeprom access
|
|
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], mear ; Eeprom access register
|
|
mov eax, EEREQ ; Request access to eeprom
|
|
out dx, eax ; Send request
|
|
|
|
;-----------------------------------------------------
|
|
; Loop 4000 times and if access not granted, error out
|
|
|
|
mov ecx, 4000
|
|
.loop:
|
|
in eax, dx ; get eeprom status
|
|
test eax, EEGNT ; see if eeprom access granted flag is set
|
|
jnz .got_access ; if it is, go access the eeprom
|
|
loop .loop ; else keep waiting
|
|
|
|
DEBUGF 2, "Access to EEprom failed!\n", 0
|
|
|
|
set_io [ebx + device.io_addr], mear ; Eeprom access register
|
|
mov eax, EEDONE ; tell eeprom we are done
|
|
out dx, eax
|
|
|
|
or eax, -1 ; error
|
|
ret
|
|
|
|
.got_access:
|
|
|
|
;------------------------------------------
|
|
; EEprom access granted, read MAC from card
|
|
|
|
; zero based so 3-16 bit reads will take place
|
|
|
|
mov ecx, 2
|
|
.read_loop:
|
|
mov eax, EEPROMMACAddr ; Base Mac Address
|
|
add eax, ecx ; Current Mac Byte Offset
|
|
push ecx
|
|
call read_eeprom ; try to read 16 bits
|
|
pop ecx
|
|
mov word [ebx + device.mac+ecx*2], ax ; save 16 bits to the MAC ID varible
|
|
dec ecx ; one less word to read
|
|
jns .read_loop ; if more read more
|
|
mov eax, 1 ; return non-zero indicating success
|
|
|
|
DEBUGF 1,"%x-%x-%x-%x-%x-%x\n",\
|
|
[ebx + device.mac]: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
|
|
|
|
;-------------------------------------
|
|
; Tell EEPROM We are Done Accessing It
|
|
|
|
.done:
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], mear ; Eeprom access register
|
|
mov eax, EEDONE ; tell eeprom we are done
|
|
out dx, eax
|
|
|
|
xor eax, eax ; ok
|
|
ret
|
|
|
|
|
|
|
|
|
|
;***************************************************************************
|
|
;
|
|
; get_mac_addr: - Get MAC address for stand alone SiS900 model
|
|
;
|
|
; Older SiS900 and friends, use EEPROM to store MAC address.
|
|
;
|
|
;***************************************************************************
|
|
align 4
|
|
SIS900_get_mac_addr:
|
|
DEBUGF 1, "SIS900 - get mac:\n"
|
|
|
|
;------------------------------------
|
|
; check to see if we have sane EEPROM
|
|
|
|
mov eax, EEPROMSignature ; Base Eeprom Signature
|
|
call read_eeprom ; try to read 16 bits
|
|
cmp ax, 0xffff
|
|
je .err
|
|
test ax, ax
|
|
je .err
|
|
|
|
;-----------
|
|
; Read MacID
|
|
|
|
; zero based so 3-16 bit reads will take place
|
|
|
|
mov ecx, 2
|
|
.loop:
|
|
mov eax, EEPROMMACAddr ; Base Mac Address
|
|
add eax, ecx ; Current Mac Byte Offset
|
|
push ecx
|
|
call read_eeprom ; try to read 16 bits
|
|
pop ecx
|
|
mov word [ebx + device.mac+ecx*2], ax ; save 16 bits to the MAC ID storage
|
|
dec ecx ; one less word to read
|
|
jns .loop ; if more read more
|
|
|
|
DEBUGF 1,"%x-%x-%x-%x-%x-%x\n",\
|
|
[ebx + device.mac]: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
|
|
|
|
xor eax, eax
|
|
ret
|
|
|
|
|
|
.err:
|
|
DEBUGF 1, "Access to EEprom failed!\n", 0
|
|
|
|
or eax, -1
|
|
ret
|
|
|
|
|
|
;***************************************************************************
|
|
;
|
|
; Get_Mac_SIS635_900_REV: - Get MAC address for model 635
|
|
;
|
|
;***************************************************************************
|
|
align 4
|
|
Get_Mac_SIS635_900_REV:
|
|
DEBUGF 1, "SIS635 - get mac:\n"
|
|
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], rfcr
|
|
in eax, dx
|
|
mov esi, eax
|
|
|
|
set_io [ebx + device.io_addr], cr
|
|
or eax, RELOAD
|
|
out dx, eax
|
|
|
|
xor eax, eax
|
|
out dx, eax
|
|
|
|
;-----------------------------------------------
|
|
; Disable packet filtering before setting filter
|
|
|
|
set_io [ebx + device.io_addr], rfcr
|
|
mov eax, esi
|
|
and eax, not RFEN
|
|
out dx, eax
|
|
|
|
;---------------------------------
|
|
; Load MAC to filter data register
|
|
|
|
xor ecx, ecx
|
|
lea edi, [ebx + device.mac]
|
|
.loop:
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], rfcr
|
|
mov eax, ecx
|
|
shl eax, RFADDR_shift
|
|
out dx, eax
|
|
|
|
set_io [ebx + device.io_addr], rfdr
|
|
in ax, dx
|
|
stosw
|
|
inc ecx
|
|
cmp ecx, 3
|
|
jb .loop
|
|
|
|
;------------------------
|
|
; Enable packet filtering
|
|
|
|
set_io [ebx + device.io_addr], rfcr
|
|
mov eax, esi
|
|
or eax, RFEN
|
|
out dx, eax
|
|
|
|
DEBUGF 1,"%x-%x-%x-%x-%x-%x\n",\
|
|
[ebx + device.mac]: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
|
|
|
|
xor eax, eax
|
|
ret
|
|
|
|
;***************************************************************************
|
|
;
|
|
; read_eeprom
|
|
;
|
|
; reads and returns a given location from EEPROM
|
|
;
|
|
; IN: si = addr
|
|
; OUT: ax = data
|
|
;
|
|
;***************************************************************************
|
|
align 4
|
|
read_eeprom:
|
|
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], mear
|
|
|
|
xor eax, eax ; start send
|
|
out dx, eax
|
|
ee_delay
|
|
|
|
or eax, EECLK
|
|
out dx, eax
|
|
ee_delay
|
|
|
|
;------------------------------------
|
|
; Send the read command
|
|
|
|
or esi, EEread
|
|
mov ecx, 1 shl 9
|
|
|
|
.loop:
|
|
mov eax, EECS
|
|
test esi, ecx
|
|
jz @f
|
|
or eax, EEDI
|
|
@@:
|
|
out dx, eax
|
|
ee_delay
|
|
|
|
or eax, EECLK
|
|
out dx, eax
|
|
ee_delay
|
|
|
|
shr esi, 1
|
|
jnc .loop
|
|
|
|
mov eax, EECS
|
|
out dx, eax
|
|
ee_delay
|
|
|
|
;------------------------
|
|
; Read 16-bits of data in
|
|
|
|
xor esi, esi
|
|
mov cx, 16
|
|
.loop2:
|
|
mov eax, EECS
|
|
out dx, eax
|
|
ee_delay
|
|
|
|
or eax, EECLK
|
|
out dx, eax
|
|
ee_delay
|
|
|
|
in eax, dx
|
|
shl esi, 1
|
|
test eax, EEDO
|
|
jz @f
|
|
inc esi
|
|
@@:
|
|
loop .loop2
|
|
|
|
;----------------------------
|
|
; Terminate the EEPROM access
|
|
|
|
xor eax, eax
|
|
out dx, eax
|
|
ee_delay
|
|
|
|
mov eax, EECLK
|
|
out dx, eax
|
|
ee_delay
|
|
|
|
movzx eax, si
|
|
|
|
ret
|
|
|
|
|
|
|
|
align 4
|
|
write_mac:
|
|
DEBUGF 2,'Setting MAC is not supported for SIS900 card.\n'
|
|
add esp, 6
|
|
ret
|
|
|
|
|
|
|
|
|
|
;***************************************************************************
|
|
; Function
|
|
; transmit
|
|
; Description
|
|
; Transmits a packet of data via the ethernet card
|
|
; buffer pointer in [esp+4]
|
|
; size of buffer in [esp+8]
|
|
; pointer to device structure in ebx
|
|
;
|
|
;***************************************************************************
|
|
align 4
|
|
|
|
proc transmit stdcall bufferptr
|
|
|
|
pushf
|
|
cli
|
|
|
|
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 .fail
|
|
cmp [esi + NET_BUFF.length], 60
|
|
jb .fail
|
|
|
|
movzx ecx, [ebx + device.cur_tx]
|
|
shl ecx, 4 ; *16
|
|
lea ecx, [ebx + device.txd + ecx]
|
|
|
|
test dword[ecx + 4], 0x80000000 ; card owns descriptor ?
|
|
jnz .fail
|
|
|
|
mov eax, esi
|
|
mov dword[ecx + 12], eax
|
|
add eax, [eax + NET_BUFF.offset]
|
|
invoke GetPhysAddr
|
|
mov dword[ecx + 8], eax ; buffer address
|
|
|
|
mov eax, [esi + NET_BUFF.length]
|
|
and eax, DSIZE
|
|
or eax, 0x80000000 ; card owns descriptor
|
|
mov dword[ecx + 4], eax ; status field
|
|
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], cr
|
|
in eax, dx
|
|
or eax, TxENA ; Enable the transmit state machine
|
|
out dx, eax
|
|
|
|
inc [ebx + device.cur_tx]
|
|
and [ebx + device.cur_tx], NUM_TX_DESC-1
|
|
|
|
; update stats
|
|
mov ecx, [esi + NET_BUFF.length]
|
|
inc [ebx + device.packets_tx]
|
|
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 NetFree, [bufferptr]
|
|
popf
|
|
or eax, -1
|
|
ret
|
|
|
|
endp
|
|
|
|
|
|
;***************************************************************************
|
|
;
|
|
; int_handler
|
|
;
|
|
; handles received IRQs, which signal received packets
|
|
;
|
|
; Currently only supports one descriptor per packet, if packet is fragmented
|
|
; between multiple descriptors you will lose part of the packet
|
|
;
|
|
;***************************************************************************
|
|
|
|
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], isr
|
|
in eax, dx ; note that this clears all interrupts
|
|
test ax, IE
|
|
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, ax
|
|
|
|
test ax, RxOK
|
|
jz .no_rx_
|
|
|
|
push ax
|
|
|
|
.rx_loop:
|
|
|
|
;-----------
|
|
; Get Status
|
|
movzx eax, [ebx + device.cur_rx] ; find current descriptor
|
|
shl eax, 4 ; * 16
|
|
mov ecx, dword[ebx + device.rxd + eax + 4] ; get receive status
|
|
|
|
;-------------------------------------------
|
|
; Check RX_Status to see if packet is waiting
|
|
test ecx, 0x80000000
|
|
jz .no_rx
|
|
|
|
;----------------------------------------------
|
|
; There is a packet waiting check it for errors
|
|
test ecx, 0x67C0000 ; see if there are any errors
|
|
jnz .error_status
|
|
|
|
;---------------------
|
|
; Check size of packet
|
|
and ecx, DSIZE ; get packet size minus CRC
|
|
sub ecx, CRC_SIZE ; make sure packet contains data
|
|
jbe .error_size
|
|
|
|
; update statistics
|
|
inc dword [ebx + device.packets_rx]
|
|
add dword [ebx + device.bytes_rx], ecx
|
|
adc dword [ebx + device.bytes_rx + 4], 0
|
|
|
|
push ebx
|
|
push .return ; return addr
|
|
mov eax, [ebx + device.rxd + eax + 12]
|
|
push eax ; packet ptr
|
|
mov [eax + NET_BUFF.length], ecx
|
|
mov [eax + NET_BUFF.device], ebx
|
|
mov [eax + NET_BUFF.offset], NET_BUFF.data
|
|
DEBUGF 1, "Packet received OK\n"
|
|
jmp [EthInput]
|
|
.return:
|
|
pop ebx
|
|
|
|
; Reset status, allow ethernet card access to descriptor
|
|
invoke NetAlloc, RX_BUFF_SZ + NET_BUFF.data
|
|
test eax, eax
|
|
jz .fail
|
|
movzx ecx, [ebx + device.cur_rx]
|
|
shl ecx, 4 ; *16
|
|
lea ecx, [ebx + device.rxd + ecx]
|
|
mov dword [ecx + 12], eax
|
|
invoke GetPhysAddr
|
|
add eax, NET_BUFF.data
|
|
mov dword [ecx + 8], eax
|
|
mov dword [ecx + 4], RX_BUFF_SZ
|
|
|
|
inc [ebx + device.cur_rx] ; get next descriptor
|
|
and [ebx + device.cur_rx], NUM_RX_DESC-1 ; only 4 descriptors 0-3
|
|
|
|
jmp .rx_loop
|
|
|
|
.no_rx:
|
|
set_io [ebx + device.io_addr], 0
|
|
set_io [ebx + device.io_addr], cr
|
|
in eax, dx
|
|
or eax, RxENA ; Re-Enable the Receive state machine
|
|
out dx, eax
|
|
|
|
pop ax
|
|
|
|
.no_rx_:
|
|
test ax, TxOK
|
|
jz .no_tx
|
|
|
|
DEBUGF 1, "TX ok!\n"
|
|
|
|
.tx_loop:
|
|
movzx ecx, [ebx + device.last_tx]
|
|
shl ecx, 4 ; *16
|
|
lea ecx, [ebx + device.txd + ecx]
|
|
|
|
test dword[ecx + 4], 0x80000000 ; card owns descr
|
|
jnz .no_tx
|
|
cmp dword[ecx + 12], 0
|
|
je .no_tx
|
|
|
|
DEBUGF 1, "Freeing packet = %x\n", [ecx + 12]:8
|
|
push dword[ecx + 12]
|
|
mov dword[ecx + 12], 0
|
|
invoke NetFree
|
|
|
|
inc [ebx + device.last_tx]
|
|
and [ebx + device.last_tx], NUM_TX_DESC-1
|
|
jmp .tx_loop
|
|
|
|
.no_tx:
|
|
.fail:
|
|
pop edi esi ebx
|
|
xor eax, eax
|
|
inc eax
|
|
|
|
ret
|
|
|
|
ret
|
|
|
|
.error_status:
|
|
DEBUGF 2, "Packet error: %x\n", ecx
|
|
jmp .fail
|
|
|
|
.error_size:
|
|
DEBUGF 2, "Packet too large/small\n"
|
|
jmp .fail
|
|
|
|
|
|
|
|
|
|
|
|
; End of code
|
|
|
|
data fixups
|
|
end data
|
|
|
|
include '../peimport.inc'
|
|
|
|
my_service db 'SIS900',0 ; max 16 chars include zero
|
|
|
|
specific_table:
|
|
; dd SIS630A_900_REV, Get_Mac_SIS630A_900_REV, 0
|
|
; dd SIS630E_900_REV, Get_Mac_SIS630E_900_REV, 0
|
|
dd SIS630S_900_REV, Get_Mac_SIS635_900_REV, 0
|
|
dd SIS630EA1_900_REV, Get_Mac_SIS635_900_REV, 0
|
|
dd SIS630ET_900_REV, Get_Mac_SIS635_900_REV, 0 ; SIS630ET_900_REV_SpecialFN
|
|
dd SIS635A_900_REV, Get_Mac_SIS635_900_REV, 0
|
|
dd SIS900_960_REV, SIS960_get_mac_addr, 0
|
|
dd SIS900B_900_REV, SIS900_get_mac_addr, 0
|
|
dd 0 ; end of list
|
|
|
|
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
|
|
|