Subversion Repositories Kolibri OS

Compare Revisions

No changes between revisions

Regard whitespace Rev 3544 → Rev 3545

/drivers/ethernet/3c59x.asm
0,0 → 1,2954
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; 3Com network driver for KolibriOS ;;
;; ;;
;; Ported to KolibriOS net-branch by hidnplayr (28/05/10) ;;
;; ;;
;; Thanks to: scrap metal recyclers, whom provide me with ;;
;; loads of hardware ;;
;; diamond: who makes me understand KolibriOS ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; 3C59X.INC ;;
;; ;;
;; Ethernet driver for Menuet OS ;;
;; ;;
;; Driver for 3Com fast etherlink 3c59x and ;;
;; etherlink XL 3c900 and 3c905 cards ;;
;; References: ;;
;; www.3Com.com - data sheets ;;
;; DP83840A.pdf - ethernet physical layer ;;
;; 3c59x.c - linux driver ;;
;; ethernet driver template by Mike Hibbett ;;
;; ;;
;; Credits ;;
;; Mike Hibbett, ;;
;; who kindly supplied me with a 3Com905C-TX-M card ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Copyright (c) 2004, Endre Kozma <endre.kozma@axelero.hu>
;; All rights reserved.
;;
;; Redistribution and use in source and binary forms, with or without
;; modification, are permitted provided that the following conditions are
;; met:
;;
;; 1. Redistributions of source code must retain the above copyright notice,
;; this list of conditions and the following disclaimer.
;;
;; 2. Redistributions in binary form must reproduce the above copyright
;; notice, this list of conditions and the following disclaimer in the
;; documentation and/or other materials provided with the distribution.
;;
;; 3. The name of the author may not be used to endorse or promote products
;; derived from this software without specific prior written permission.
;;
;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
;; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;;
;; History
;; =======
;; $Log: 3C59X.INC,v $
;; Revision 1.3 2004/07/11 12:21:12 kozma
;; Support of vortex chips (3c59x) added.
;; Support of 3c920 and 3c982 added.
;; Corrections.
;;
;; Revision 1.2 2004/06/12 19:40:20 kozma
;; Function e3c59x_set_available_media added in order to set
;; the default media in case auto detection finds no valid link.
;; Incorrect mii check removed (3c900 Cyclone works now).
;; Cleanups.
;;
;; Revision 1.1 2004/06/12 18:27:15 kozma
;; Initial revision
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
format MS COFF
 
API_VERSION = 0x01000100
DRIVER_VERSION = 5
 
MAX_DEVICES = 16
FORCE_FD = 0 ; forcing full duplex mode makes sense at some cards and link types
PROMISCIOUS = 0 ; enables promiscous mode
 
DEBUG = 1
__DEBUG__ = 1
__DEBUG_LEVEL__ = 2
 
include '../proc32.inc'
include '../imports.inc'
include '../fdo.inc'
include '../netdrv.inc'
 
public START
public service_proc
public version
 
struc DPD { ; Download Packet Descriptor
 
.next_ptr dd ?
.frame_start_hdr dd ?
.frag_addr dd ? ; for packet data
.frag_len dd ? ; for packet data
.realaddr dd ?
.size = 32
}
 
virtual at 0
dpd DPD
end virtual
 
 
struc UPD { ; Upload Packet Descriptor
 
.next_ptr dd ?
.pkt_status dd ?
.frag_addr dd ?
.frag_len dd ? ; for packet data
.realaddr dd ?
.size = 32
 
}
 
virtual at 0
upd UPD
end virtual
 
; Registers
REG_POWER_MGMT_CTRL = 0x7c
REG_UP_LIST_PTR = 0x38
REG_UP_PKT_STATUS = 0x30
REG_TX_FREE_THRESH = 0x2f
REG_DN_LIST_PTR = 0x24
REG_DMA_CTRL = 0x20
REG_TX_STATUS = 0x1b
REG_RX_STATUS = 0x18
REG_TX_DATA = 0x10
 
; Common window registers
REG_INT_STATUS = 0xe
REG_COMMAND = 0xe
 
; Register window 7
REG_MASTER_STATUS = 0xc
REG_POWER_MGMT_EVENT = 0xc
REG_MASTER_LEN = 0x6
REG_VLAN_ETHER_TYPE = 0x4
REG_VLAN_MASK = 0x0
REG_MASTER_ADDRESS = 0x0
 
; Register window 6
REG_BYTES_XMITTED_OK = 0xc
REG_BYTES_RCVD_OK = 0xa
REG_UPPER_FRAMES_OK = 0x9
REG_FRAMES_DEFERRED = 0x8
REG_FRAMES_RCVD_OK = 0x7
REG_FRAMES_XMITTED_OK = 0x6
REG_RX_OVERRUNS = 0x5
REG_LATE_COLLISIONS = 0x4
REG_SINGLE_COLLISIONS = 0x3
REG_MULTIPLE_COLLISIONS = 0x2
REG_SQE_ERRORS = 0x1
REG_CARRIER_LOST = 0x0
 
; Register window 5
REG_INDICATION_ENABLE = 0xc
REG_INTERRUPT_ENABLE = 0xa
REG_TX_RECLAIM_THRESH = 0x9
REG_RX_FILTER = 0x8
REG_RX_EARLY_THRESH = 0x6
REG_TX_START_THRESH = 0x0
 
; Register window 4
REG_UPPER_BYTES_OK = 0xe
REG_BAD_SSD = 0xc
REG_MEDIA_STATUS = 0xa
REG_PHYSICAL_MGMT = 0x8
REG_NETWORK_DIAGNOSTIC = 0x6
REG_FIFO_DIAGNOSTIC = 0x4
REG_VCO_DIAGNOSTIC = 0x2 ; may not supported
 
; Bits in register window 4
BIT_AUTOSELECT = 24
 
; Register window 3
REG_TX_FREE = 0xc
REG_RX_FREE = 0xa
REG_MEDIA_OPTIONS = 0x8
REG_MAC_CONTROL = 0x6
REG_MAX_PKT_SIZE = 0x4
REG_INTERNAL_CONFIG = 0x0
 
; Register window 2
REG_RESET_OPTIONS = 0xc
REG_STATION_MASK_HI = 0xa
REG_STATION_MASK_MID = 0x8
REG_STATION_MASK_LO = 0x6
REG_STATION_ADDRESS_HI = 0x4
REG_STATION_ADDRESS_MID = 0x2
REG_STATION_ADDRESS_LO = 0x0
 
; Register window 1
REG_TRIGGER_BITS = 0xc
REG_SOS_BITS = 0xa
REG_WAKE_ON_TIMER = 0x8
REG_SMB_RXBYTES = 0x7
REG_SMB_DIAG = 0x5
REG_SMB_ARB = 0x4
REG_SMB_STATUS = 0x2
REG_SMB_ADDRESS = 0x1
REG_SMB_FIFO_DATA = 0x0
 
; Register window 0
REG_EEPROM_DATA = 0xc
REG_EEPROM_COMMAND = 0xa
REG_BIOS_ROM_DATA = 0x8
REG_BIOS_ROM_ADDR = 0x4
 
; Physical management bits
BIT_MGMT_DIR = 2 ; drive with the data written in mgmtData
BIT_MGMT_DATA = 1 ; MII management data bit
BIT_MGMT_CLK = 0 ; MII management clock
 
; MII commands
MII_CMD_MASK = (1111b shl 10)
MII_CMD_READ = (0110b shl 10)
MII_CMD_WRITE = (0101b shl 10)
 
; MII registers
REG_MII_BMCR = 0 ; basic mode control register
REG_MII_BMSR = 1 ; basic mode status register
REG_MII_ANAR = 4 ; auto negotiation advertisement register
REG_MII_ANLPAR = 5 ; auto negotiation link partner ability register
REG_MII_ANER = 6 ; auto negotiation expansion register
 
; MII bits
BIT_MII_AUTONEG_COMPLETE = 5 ; auto-negotiation complete
BIT_MII_PREAMBLE_SUPPRESSION = 6
 
; eeprom bits and commands
EEPROM_CMD_READ = 0x80
EEPROM_BIT_BUSY = 15
 
; eeprom registers
EEPROM_REG_OEM_NODE_ADDR= 0xa
EEPROM_REG_CAPABILITIES = 0x10
 
; Commands for command register
SELECT_REGISTER_WINDOW = (1 shl 11)
 
IS_VORTEX = 0x1
IS_BOOMERANG = 0x2
IS_CYCLONE = 0x4
IS_TORNADO = 0x8
EEPROM_8BIT = 0x10
HAS_PWR_CTRL = 0x20
HAS_MII = 0x40
HAS_NWAY = 0x80
HAS_CB_FNS = 0x100
INVERT_MII_PWR = 0x200
INVERT_LED_PWR = 0x400
MAX_COLLISION_RESET = 0x800
EEPROM_OFFSET = 0x1000
HAS_HWCKSM = 0x2000
EXTRA_PREAMBLE = 0x4000
 
; Status
IntLatch = 0x0001
HostError = 0x0002
TxComplete = 0x0004
TxAvailable = 0x0008
RxComplete = 0x0010
RxEarly = 0x0020
IntReq = 0x0040
StatsFull = 0x0080
DMADone = 0x0100
DownComplete = 0x0200
UpComplete = 0x0400
DMAInProgress = 0x0800 ; 1 shl 11 (DMA controller is still busy)
CmdInProgress = 0x1000 ; 1 shl 12 (EL3_CMD is still busy)
 
S_5_INTS = HostError + RxEarly + UpComplete + DownComplete ;+ TxComplete + RxComplete + TxAvailable
 
; Commands
TotalReset = 0 shl 11
SelectWindow = 1 shl 11
StartCoax = 2 shl 11
RxDisable = 3 shl 11
RxEnable = 4 shl 11
RxReset = 5 shl 11
UpStall = 6 shl 11
UpUnstall = (6 shl 11)+1
DownStall = (6 shl 11)+2
DownUnstall = (6 shl 11)+3
RxDiscard = 8 shl 11
TxEnable = 9 shl 11
TxDisable = 10 shl 11
TxReset = 11 shl 11
FakeIntr = 12 shl 11
AckIntr = 13 shl 11
SetIntrEnb = 14 shl 11
SetStatusEnb = 15 shl 11
SetRxFilter = 16 shl 11
SetRxThreshold = 17 shl 11
SetTxThreshold = 18 shl 11
SetTxStart = 19 shl 11
StartDMAUp = 20 shl 11
StartDMADown = (20 shl 11)+1
StatsEnable = 21 shl 11
StatsDisable = 22 shl 11
StopCoax = 23 shl 11
SetFilterBit = 25 shl 11
 
; Rx mode bits
RxStation = 1
RxMulticast = 2
RxBroadcast = 4
RxProm = 8
 
; RX/TX buffers sizes
MAX_ETH_PKT_SIZE = 1536 ; max packet size
NUM_RX_DESC = 4 ; a power of 2 number
NUM_TX_DESC = 4 ; a power of 2 number
MAX_ETH_FRAME_SIZE = 1520 ; size of ethernet frame + bytes alignment
 
virtual at ebx
 
device:
 
ETH_DEVICE
 
.dpd_buffer rd (dpd.size*NUM_TX_DESC)/4
.upd_buffer rd (upd.size*NUM_RX_DESC)/4
.curr_upd dd ?
.prev_dpd dd ?
 
.io_addr dd ?
.pci_bus dd ?
.pci_dev dd ?
.irq_line db ?
rb 3 ; alignment
 
.prev_tx_frame dd ?
.ver_id db ?
.full_bus_master db ?
.has_hwcksm db ?
.preamble db ?
.dn_list_ptr_cleared db ?
 
.size = $ - device
 
end virtual
 
section '.flat' code readable align 16
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; proc START ;;
;; ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
proc START stdcall, state:dword
 
cmp [state], 1
jne .exit
 
.entry:
 
DEBUGF 1,"Loading %s driver\n", my_service
stdcall RegService, my_service, service_proc
ret
 
.fail:
.exit:
xor eax, eax
ret
 
endp
 
 
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; proc SERVICE_PROC ;;
;; ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
proc service_proc stdcall, ioctl:dword
 
mov edx, [ioctl]
mov eax, [IOCTL.io_code]
 
;------------------------------------------------------
 
cmp eax, 0 ;SRV_GETVERSION
jne @F
 
cmp [IOCTL.out_size], 4
jb .fail
mov eax, [IOCTL.output]
mov [eax], dword API_VERSION
 
xor eax, eax
ret
 
;------------------------------------------------------
@@:
cmp eax, 1 ;SRV_HOOK
jne .fail
 
cmp [IOCTL.inp_size], 3 ; Data input must be at least 3 bytes
jb .fail
 
mov eax, [IOCTL.input]
cmp byte [eax], 1 ; 1 means device number and bus number (pci) are given
jne .fail ; other types of this hardware dont exist
 
; check if the device is already listed
 
mov ecx, [VORTEX_DEVICES]
test ecx, ecx
jz .maybeboomerang
 
mov esi, VORTEX_LIST
mov eax, [IOCTL.input] ; get the pci bus and device numbers
mov ax , [eax+1] ;
.nextdevice:
mov ebx, [esi]
cmp al, byte[device.pci_bus]
jne @f
cmp ah, byte[device.pci_dev]
je .find_devicenum ; Device is already loaded, let's find it's device number
@@:
add esi, 4
loop .nextdevice
 
 
.maybeboomerang:
mov ecx, [BOOMERANG_DEVICES]
test ecx, ecx
jz .firstdevice
 
mov esi, BOOMERANG_LIST
mov eax, [IOCTL.input] ; get the pci bus and device numbers
mov ax , [eax+1] ;
.nextdevice2:
mov ebx, [esi]
cmp al, byte[device.pci_bus]
jne @f
cmp ah, byte[device.pci_dev]
je .find_devicenum ; Device is already loaded, let's find it's device number
@@:
add esi, 4
loop .nextdevice2
 
 
; This device doesnt have its own eth_device structure yet, lets create one
.firstdevice:
mov ecx, [BOOMERANG_DEVICES]
add ecx, [VORTEX_DEVICES]
cmp ecx, MAX_DEVICES ; First check if the driver can handle one more card
jae .fail
 
allocate_and_clear ebx, device.size, .fail ; Allocate the buffer for device structure
 
; Fill in the direct call addresses into the struct
 
mov [device.reset], reset
mov [device.transmit], null_op
mov [device.unload], null_op
mov [device.name], my_service
 
; save the pci bus and device numbers
 
mov eax, [IOCTL.input]
movzx ecx, byte[eax+1]
mov [device.pci_bus], ecx
movzx ecx, byte[eax+2]
mov [device.pci_dev], ecx
 
; Now, it's time to find the base io addres of the PCI device
PCI_find_io
 
; We've found the io address, find IRQ now
PCI_find_irq
 
DEBUGF 1,"Hooking into device, dev:%x, bus:%x, irq:%x, addr:%x\n",\
[device.pci_dev]:1,[device.pci_bus]:1,[device.irq_line]:1,[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 occured, exit
 
 
movzx ecx, [device.ver_id]
test word [hw_versions+2+ecx*4], IS_VORTEX
jz .not_vortex
 
mov eax, [VORTEX_DEVICES] ; Add the device structure to our device list
mov [VORTEX_LIST+4*eax], ebx ; (IRQ handler uses this list to find device)
inc [VORTEX_DEVICES] ;
 
.register:
mov [device.type], NET_TYPE_ETH
call NetRegDev
 
cmp eax, -1
je .destroy
 
call start_device
ret
 
.not_vortex:
mov eax, [BOOMERANG_DEVICES] ; Add the device structure to our device list
mov [BOOMERANG_LIST+4*eax], ebx ; (IRQ handler uses this list to find device)
inc [BOOMERANG_DEVICES]
 
jmp .register
 
; 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"
call NetPtrToNum ; This kernel procedure converts a pointer to device struct in ebx
; into a device number in edi
mov eax, edi ; Application wants it in eax instead
DEBUGF 1,"Kernel says: %u\n", eax
ret
 
; If an error occured, remove all allocated data and exit (returning -1 in eax)
 
.destroy:
; todo: reset device into virgin state
 
.err:
stdcall KernelFree, ebx
 
 
.fail:
or eax, -1
ret
 
;------------------------------------------------------
endp
 
 
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
;; ;;
;; Actual Hardware dependent code starts here ;;
;; ;;
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
 
 
 
 
 
;***************************************************************************
; Function
; probe
; Description
; Searches for an ethernet card, enables it and clears the rx buffer
; Destroyed registers
; eax, ebx, ecx, edx, edi, esi
;
;***************************************************************************
 
align 4
probe:
 
DEBUGF 1,"Probing 3com card\n"
 
PCI_make_bus_master
 
; wake up the card
call wake_up
 
stdcall PciRead32, [device.pci_bus], [device.pci_dev], 0 ; get device/vendor id
 
DEBUGF 1,"Vendor id: 0x%x\n", ax
 
cmp ax, 0x10B7
jne .notfound
shr eax, 16
 
DEBUGF 1,"Vendor ok!, device id: 0x%x\n", ax
 
; get chip version
mov ecx, HW_VERSIONS_SIZE/4-1
.loop:
cmp ax, [hw_versions+ecx*4]
jz .found
loop .loop
.notfound:
DEBUGF 1,"Device id not found in list!\n"
or eax, -1
ret
.found:
mov esi, [hw_str+ecx*4]
DEBUGF 1,"Hardware type: %s\n", esi
mov [device.name], esi
 
mov [device.ver_id], cl
test word [hw_versions+2+ecx*4], HAS_HWCKSM
setnz [device.has_hwcksm]
; set pci latency for vortex cards
test word [hw_versions+2+ecx*4], IS_VORTEX
jz .not_vortex
 
mov eax, 11111000b ; 248 = max latency
stdcall PciWrite32, [device.pci_bus], [device.pci_dev], PCI_REG_LATENCY, eax
 
.not_vortex:
; set RX/TX functions
mov ax, EEPROM_REG_CAPABILITIES
call read_eeprom
test al, 100000b ; full bus master?
setnz [device.full_bus_master]
jnz .boomerang_func
mov [device.transmit], vortex_transmit
DEBUGF 1,"Device is a vortex type\n"
DEBUGF 1,"I'm sorry but vortex code hasnt been tested yet\n"
DEBUGF 1,"Please contact me on hidnplayr@kolibrios.org\n"
DEBUGF 1,"If you can help me finish it!\n"
or eax, -1
ret
jmp @f
.boomerang_func: ; full bus master, so use boomerang functions
mov [device.transmit], boomerang_transmit
DEBUGF 1,"Device is a boomerang type\n"
@@:
call read_mac_eeprom
 
test byte [device.full_bus_master], 0xff
jz .set_preamble
; switch to register window 2
set_io 0
set_io REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+2
out dx, ax
; activate xcvr by setting some magic bits
set_io REG_RESET_OPTIONS
in ax, dx
and ax, not 0x4010
movzx ecx, [device.ver_id]
test word [ecx*4+hw_versions+2], INVERT_LED_PWR
jz @f
or al, 0x10
@@:
test word [ecx*4+hw_versions+2], INVERT_MII_PWR
jz @f
or ah, 0x40
@@:
out dx, ax
.set_preamble:
; use preamble as default
mov byte [device.preamble], 1 ; enable preamble
 
call global_reset
 
;--------------------------
; RESET
 
align 4
reset:
 
movzx eax, [device.irq_line]
DEBUGF 1,"Attaching int handler to irq %x\n",eax:1
 
movzx ecx, [device.ver_id]
test word [hw_versions+2+ecx*4], IS_VORTEX
jz .not_vortex
 
mov esi, int_vortex
jmp .reg_int
 
.not_vortex:
mov esi, int_boomerang
 
.reg_int:
stdcall AttachIntHandler, eax, esi, dword 0
test eax, eax
jnz @f
DEBUGF 1,"\nCould not attach int handler!\n"
; or eax, -1
; ret
@@:
 
set_io 0
set_io REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW + 0
out dx, ax
 
mov ax, StopCoax
out dx, ax ; stop transceiver
 
mov ax, SELECT_REGISTER_WINDOW + 4
out dx, ax ; disable UTP
 
set_io REG_MEDIA_STATUS
mov ax, 0x0
 
set_io REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW + 0
out dx, ax
 
set_io REG_FIFO_DIAGNOSTIC
mov ax, 0
out dx, ax ; disable card
 
mov ax, 1
out dx, ax ; enable card
 
call write_mac
 
 
;<<<<<<<<<<<<<<
 
set_io REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW + 1
out dx, ax
 
mov ecx, 32
set_io 0x0b
.loop:
in al, dx
loop .loop
 
; Get rid of stray ints
set_io REG_COMMAND
mov ax, AckIntr + 0xff
out dx, ax
 
mov ax, SetStatusEnb + S_5_INTS
out dx, ax
 
mov ax, SetIntrEnb + S_5_INTS
out dx, ax
 
call set_rx_mode
call set_active_port
 
;>>>>>>>>>>
 
call create_rx_ring
call rx_reset
call tx_reset
 
;>>>>>>>>>>>>>>>>>>
 
xor eax, eax
; clear packet/byte counters
 
lea edi, [device.bytes_tx]
mov ecx, 6
rep stosd
 
; Set the mtu, kernel will be able to send now
mov [device.mtu], 1514
 
ret
 
 
 
 
 
align 4
start_device:
DEBUGF 1,"Starting the device\n"
 
set_io 0
set_io REG_COMMAND
mov ax, SetTxThreshold + 60 ;2047 ; recommended by the manual :)
out dx, ax
 
call check_tx_status
 
set_io 0
set_io REG_COMMAND
; switch to register window 4
mov ax, SELECT_REGISTER_WINDOW+4
out dx, ax
 
; wait for linkDetect
set_io REG_MEDIA_STATUS
mov ecx, 20 ; wait for max 2s
.link_detect_loop:
mov esi, 100
call Sleep ; 100 ms
in ax, dx
test ah, 1000b ; linkDetect
jnz @f
loop .link_detect_loop
DEBUGF 1,"Link detect timed-out!\n"
@@:
 
; print link type
xor eax, eax
bsr ax, word [device.state]
jz @f
sub ax, 4
@@:
 
mov esi, [link_str+eax*4]
DEBUGF 1,"Established Link type: %s\n", esi
 
; enable interrupts
 
set_io REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW + 1
out dx, ax
 
mov ax, AckIntr + 0xff
out dx, ax
 
mov ax, SetStatusEnb + S_5_INTS
out dx, ax
 
mov ax, SetIntrEnb + S_5_INTS
out dx, ax
 
; Start RX/TX
 
set_io 0
set_io REG_COMMAND
mov ax, RxEnable
out dx, ax
 
mov ax, TxEnable
out dx, ax
 
set_io REG_COMMAND
mov ax, SetRxThreshold + 208
out dx, ax
 
mov ax, SetTxThreshold + 60 ;16 ; recommended by the manual :)
out dx, ax
 
mov ax, SELECT_REGISTER_WINDOW + 1
out dx, ax
 
ret
 
 
 
 
 
 
 
align 4
set_rx_mode:
 
DEBUGF 1,"Setting RX mode\n"
 
set_io 0
set_io REG_COMMAND
 
if defined PROMISCIOUS
mov ax, SetRxFilter + RxStation + RxMulticast + RxBroadcast + RxProm
else if defined ALLMULTI
mov ax, SetRxFilter + RxStation + RxMulticast + RxBroadcast
else
mov ax, SetRxFilter + RxStation + RxBroadcast
end if
out dx, ax
 
ret
 
 
 
 
 
;***************************************************************************
; Function
; global_reset
; Description
; resets the device
; Parameters:
; ebp - io_addr
; Return value:
; Destroyed registers
; ax, ecx, edx, esi
;
;***************************************************************************1
 
align 4
global_reset:
 
DEBUGF 1,"Global reset..\n"
 
; GlobalReset
set_io 0
set_io REG_COMMAND
xor eax, eax
; or al, 0x14
out dx, ax
; wait for GlobalReset to complete
mov ecx, 64000
.loop:
in ax , dx
test ah , 10000b ; check CmdInProgress
loopz .loop
 
DEBUGF 1,"Waiting for nic to boot..\n"
; wait for 2 seconds for NIC to boot
mov esi, 2000
call Sleep ; 2 seconds
 
DEBUGF 1,"Ok!\n"
 
ret
 
 
 
;***************************************************************************
; Function
; tx_reset
; Description
; resets and enables transmitter engine
;
;***************************************************************************
 
align 4
tx_reset:
DEBUGF 1,"tx reset\n"
 
; TxReset
set_io 0
set_io REG_COMMAND
mov ax, TxReset
out dx, ax
; Wait for TxReset to complete
mov ecx, 200000
.tx_reset_loop:
in ax, dx
test ah, 10000b ; check CmdInProgress
jz .tx_set_prev
dec ecx
jnz .tx_reset_loop
.tx_set_prev:
; init last_dpd
lea eax, [device.dpd_buffer + (NUM_TX_DESC-1)*dpd.size]
mov [device.prev_dpd], eax
 
.tx_enable:
ret
 
 
 
;***************************************************************************
; Function
; rx_reset
; Description
; resets and enables receiver engine
;
;***************************************************************************
 
align 4
rx_reset:
 
DEBUGF 1,"rx reset\n"
 
set_io 0
set_io REG_COMMAND
mov ax, RxReset or 0x4
out dx, ax
 
; wait for RxReset to complete
mov ecx, 200000
.loop:
in ax, dx
test ah, 10000b ; check CmdInProgress
jz .done
dec ecx
jnz .loop
.done:
 
lea eax, [device.upd_buffer]
mov [device.curr_upd], eax
GetRealAddr
set_io 0
set_io REG_UP_LIST_PTR
out dx, eax
 
.rx_enable:
ret
 
 
align 4
create_rx_ring:
; create upd ring
lea eax, [device.upd_buffer]
GetRealAddr
mov edi, eax ; real addr of first descr
 
lea esi, [device.upd_buffer] ; ptr to first descr
lea edx, [device.upd_buffer + (NUM_RX_DESC-1)*upd.size] ; ptr to last descr
 
mov ecx, NUM_RX_DESC
 
.upd_loop:
mov [edx + upd.next_ptr], edi
 
push ecx edx
stdcall KernelAlloc, MAX_ETH_FRAME_SIZE
pop edx ecx
mov [esi + upd.realaddr], eax
call GetPgAddr
mov [esi + upd.frag_addr], eax
and [esi + upd.pkt_status], 0
mov [esi + upd.frag_len], MAX_ETH_FRAME_SIZE or (1 shl 31)
 
DEBUGF 1,"UPD: lin=%x phys=%x len=%x next ptr=%x\n", [esi+upd.realaddr]:8, [esi+upd.frag_addr]:8, [esi+upd.frag_len]:8, edi
DEBUGF 1,"UPD: cur=%x prev=%x\n", esi, edx
 
mov edx, esi
add esi, upd.size
add edi, upd.size
dec ecx
jnz .upd_loop
 
ret
 
 
 
;---------------------------------------------------------------------------
; Function
; try_link_detect
; Description
; try_link_detect checks if link exists
; Parameters
; ebx = device structure
; Return value
; al - 0 ; no link detected
; al - 1 ; link detected
; Destroyed registers
; eax, ebx, ecx, edx, edi, esi
;
;---------------------------------------------------------------------------
 
align 4
try_link_detect:
 
DEBUGF 1,"trying to detect link\n"
 
; create self-directed packet
stdcall KernelAlloc, 20 ; create a buffer for the self-directed packet
test eax, eax
jz .fail
 
pushd 20 ; Packet parameters for device.transmit
push eax ;
 
mov edi, eax
 
lea esi, [device.mac]
movsw
movsd
sub esi, 6
movsw
movsd
mov ax , 0x0608
stosw
 
; download self-directed packet
call [device.transmit]
 
; switch to register window 4
set_io 0
set_io REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+4
out dx, ax
 
; See if we have received the packet by now..
cmp [device.packets_rx], 0
jnz .link_detected
 
; switch to register window 4
set_io REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+4
out dx, ax
 
; read linkbeatdetect
set_io REG_MEDIA_STATUS
in ax, dx
test ah, 1000b ; test linkBeatDetect
jnz .link_detected
xor al, al
jmp .finish
 
.link_detected:
DEBUGF 1,"link detected!\n"
setb al
 
.finish:
test al, al
jz @f
or byte [device.state+1], 100b
@@:
ret
 
.fail:
ret
 
 
 
;***************************************************************************
; Function
; try_phy
; Description
; try_phy checks the auto-negotiation function
; in the PHY at PHY index. It can also be extended to
; include link detection for non-IEEE 802.3u
; auto-negotiation devices, for instance the BCM5000. ; TODO: BCM5000
; Parameters
; ah - PHY index
; ebx - device stucture
; Return value
; al - 0 link is auto-negotiated
; al - 1 no link is auto-negotiated
; Destroyed registers
; eax, ebx, ecx, edx, esi
;
;***************************************************************************
 
align 4
try_phy:
 
DEBUGF 1,"PHY=%u\n", ah
DEBUGF 1,"Detecting if device is auto-negotiation capable\n"
 
mov al, REG_MII_BMCR
push eax
call mdio_read ; returns with window #4
or ah , 0x80 ; software reset
mov esi, eax
mov eax, dword [esp]
call mdio_write ; returns with window #4
 
; wait for reset to complete
mov esi, 2000
stdcall Sleep ; 2s
mov eax, [esp]
call mdio_read ; returns with window #4
test ah , 0x80
jnz .fail1
mov eax, [esp]
 
; wait for a while after reset
mov esi, 20
stdcall Sleep ; 20ms
mov eax, [esp]
mov al , REG_MII_BMSR
call mdio_read ; returns with window #4
test al , 1 ; extended capability supported?
jz .fail2
 
; auto-neg capable?
test al , 1000b
jz .fail2 ; not auto-negotiation capable
 
DEBUGF 1,"Device is auto-negotiation capable\n"
 
; auto-neg complete?
test al , 100000b
jnz .auto_neg_ok
 
DEBUGF 1,"Restarting auto-negotiation\n"
 
; restart auto-negotiation
mov eax, [esp]
mov al , REG_MII_ANAR
push eax
call mdio_read ; returns with window #4
or ax , 1111b shl 5; advertise only 10base-T and 100base-TX
mov esi, eax
pop eax
call mdio_write ; returns with window #4
mov eax, [esp]
call mdio_read ; returns with window #4
mov esi, eax
or bh , 10010b ; restart auto-negotiation
mov eax, [esp]
call mdio_write ; returns with window #4
mov esi, 4000
stdcall Sleep ; 4 seconds
mov eax, [esp]
mov al , REG_MII_BMSR
call mdio_read ; returns with window #4
test al , 100000b ; auto-neg complete?
jnz .auto_neg_ok
jmp .fail3
.auto_neg_ok:
 
DEBUGF 1,"Auto-negotiation complete\n"
 
; compare advertisement and link partner ability registers
mov eax, [esp]
mov al , REG_MII_ANAR
call mdio_read ; returns with window #4
xchg eax, [esp]
mov al , REG_MII_ANLPAR
call mdio_read ; returns with window #4
pop esi
and eax, esi
and eax, 1111100000b
push eax
 
mov word[device.state+2], ax
 
; switch to register window 3
set_io 0
set_io REG_COMMAND
mov ax , SELECT_REGISTER_WINDOW+3
out dx , ax
 
; set full-duplex mode
set_io REG_MAC_CONTROL
in ax , dx
and ax , not 0x120 ; clear full duplex and flow control
pop esi
test esi, 1010b shl 5; check for full-duplex
jz .half_duplex
or ax , 0x120 ; set full duplex and flow control
.half_duplex:
DEBUGF 1,"Using half-duplex\n"
out dx , ax
mov al , 1
ret
 
 
.fail1:
DEBUGF 1,"reset failed!\n"
pop eax
xor al, al
ret
 
.fail2:
DEBUGF 1,"This device is not auto-negotiation capable!\n"
pop eax
xor al, al
ret
 
.fail3:
DEBUGF 1,"auto-negotiation reset failed!\n"
pop eax
xor al, al
ret
 
 
 
;***************************************************************************
; Function
; try_mii
; Description
; try_MII checks the on-chip auto-negotiation logic
; or an off-chip MII PHY, depending upon what is set in
; xcvrSelect by the caller.
; It exits when it finds the first device with a good link.
; Parameters
; ebp - io_addr
; Return value
; al - 0
; al - 1
; Destroyed registers
; eax, ebx, ecx, edx, esi
;
;***************************************************************************
 
align 4
try_mii:
 
DEBUGF 1,"trying to find MII PHY\n"
 
; switch to register window 3
set_io 0
set_io REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+3
out dx, ax
set_io REG_INTERNAL_CONFIG
in eax, dx
and eax, (1111b shl 20)
cmp eax, (1000b shl 20) ; is auto-negotiation set?
jne .mii_device
 
DEBUGF 1,"auto-negotiation is set\n"
; switch to register window 4
set_io REG_COMMAND
mov ax , SELECT_REGISTER_WINDOW+4
out dx , ax
 
; PHY==24 is the on-chip auto-negotiation logic
; it supports only 10base-T and 100base-TX
mov ah , 24
call try_phy
test al , al
jz .fail_finish
 
mov cl , 24
jmp .check_preamble
 
.mii_device:
cmp eax, (0110b shl 20)
jne .fail_finish
 
set_io 0
set_io REG_COMMAND
mov ax , SELECT_REGISTER_WINDOW+4
out dx , ax
 
set_io REG_PHYSICAL_MGMT
in ax , dx
and al , (1 shl BIT_MGMT_DIR) or (1 shl BIT_MGMT_DATA)
cmp al , (1 shl BIT_MGMT_DATA)
je .search_for_phy
 
xor al , al
ret
 
.search_for_phy:
; search for PHY
mov cx , 31
.search_phy_loop:
DEBUGF 1,"Searching the PHY\n"
cmp cx , 24
je .next_phy
mov ah , cl ; ah = phy
mov al , REG_MII_BMCR ; al = Basic Mode Status Register
push cx
call mdio_read
pop cx
test ax , ax
jz .next_phy
cmp ax , 0xffff
je .next_phy
mov ah , cl ; ah = phy
push cx
call try_phy
pop cx
test al , al
jnz .check_preamble
.next_phy:
loopw .search_phy_loop
 
.fail_finish:
xor al, al
ret
 
; epilog
.check_preamble:
DEBUGF 1,"Using PHY: %u\nChecking PreAmble\n", cl
push eax ; eax contains the return value of try_phy
; check hard coded preamble forcing
movzx eax, [device.ver_id]
test word [eax*4+hw_versions+2], EXTRA_PREAMBLE
setnz [device.preamble] ; force preamble
jnz .finish
 
; check mii for preamble suppression
mov ah, cl
mov al, REG_MII_BMSR
call mdio_read
test al, 1000000b ; preamble suppression?
setz [device.preamble] ; no
 
.finish:
pop eax
ret
 
 
 
;***************************************************************************
; Function
; test_packet
; Description
; try_loopback try a loopback packet for 10BASE2 or AUI port
; Parameters
; ebx = device structure
;
;***************************************************************************
 
align 4
test_packet:
 
DEBUGF 1,"sending test packet\n"
 
; switch to register window 3
set_io 0
set_io REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+3
out dx, ax
 
; set fullDuplexEnable in MacControl register
set_io REG_MAC_CONTROL
in ax, dx
or ax, 0x120
out dx, ax
 
; switch to register window 5
set_io REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+5
out dx, ax
 
; set RxFilter to enable individual address matches
mov ax, (10000b shl 11)
set_io REG_RX_FILTER
in al, dx
or al, 1
set_io REG_COMMAND
out dx, ax
 
; issue RxEnable and TxEnable
call rx_reset
call tx_reset
 
; create self-directed packet
stdcall KernelAlloc, 20 ; create a buffer for the self-directed packet
test eax, eax
jz .fail
 
pushd 20 ; Packet parameters for device.transmit
push eax ;
 
mov edi, eax
lea esi, [device.mac]
movsw
movsd
sub esi, 6
movsw
movsd
mov ax , 0x0608
stosw
 
; download self-directed packet
call [device.transmit]
 
; wait for 2s
mov esi, 2000
call Sleep
 
; check if self-directed packet is received
mov eax, [device.packets_rx]
test eax, eax
jnz .finish
 
; switch to register window 3
set_io 0
set_io REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+3
out dx, ax
 
; clear fullDuplexEnable in MacControl register
set_io REG_MAC_CONTROL
in ax , dx
and ax , not 0x120
out dx , ax
.fail:
xor eax, eax
 
.finish:
ret
 
 
 
;***************************************************************************
; Function
; try_loopback
; Description
; tries a loopback packet for 10BASE2 or AUI port
; Parameters
; al - 0: 10Mbps AUI connector
; 1: 10BASE-2
; ebp - io_addr
; Return value
; al - 0
; al - 1
; Destroyed registers
; eax, ebx, ecx, edx, edi, esi
;
;***************************************************************************
 
align 4
try_loopback:
 
DEBUGF 1,"trying loopback\n"
 
push eax
; switch to register window 3
set_io 0
set_io REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+3
out dx, ax
mov eax, [esp]
 
mov cl, al
inc cl
shl cl, 3
or byte [device.state+1], cl
 
test al, al ; aui or coax?
jz .complete_loopback
; enable 100BASE-2 DC-DC converter
mov ax, (10b shl 11) ; EnableDcConverter
out dx, ax
.complete_loopback:
 
mov cx, 2 ; give a port 3 chances to complete a loopback
.next_try:
push ecx
call test_packet
pop ecx
test eax, eax
loopzw .next_try
 
.finish:
xchg eax, [esp]
test al, al
jz .aui_finish
 
; issue DisableDcConverter command
set_io 0
set_io REG_COMMAND
mov ax, (10111b shl 11)
out dx, ax
.aui_finish:
pop eax ; al contains the result of operation
 
test al, al
jnz @f
and byte [device.state+1], not 11000b
@@:
 
ret
 
 
;***************************************************************************
; Function
; set_active_port
; Description
; It selects the media port (transceiver) to be used
; Return value:
; Destroyed registers
; eax, ebx, ecx, edx, edi, esi
;
;***************************************************************************
 
align 4
set_active_port:
 
DEBUGF 1,"Trying to find the active port\n"
 
; switch to register window 3
set_io 0
set_io REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW + 3
out dx, ax
 
set_io REG_INTERNAL_CONFIG
in eax, dx
test eax, (1 shl 24) ; check if autoselect enable
jz .set_first_available_media
 
; check 100BASE-TX and 10BASE-T
set_io REG_MEDIA_OPTIONS
in ax, dx
test al, 1010b ; check whether 100BASE-TX or 10BASE-T available
jz .mii_device ; they are not available
 
; set auto-negotiation
set_io REG_INTERNAL_CONFIG
in eax, dx
and eax, not (1111b shl 20)
or eax, (1000b shl 20)
out dx, eax
call try_mii
test al, al
jz .mii_device
DEBUGF 1,"Using auto negotiation\n"
ret
 
.mii_device:
; switch to register window 3
set_io 0
; check for off-chip mii device
set_io REG_MEDIA_OPTIONS
in ax, dx
test al, 1000000b ; check miiDevice
jz .base_fx
set_io REG_INTERNAL_CONFIG
in eax, dx
and eax, not (1111b shl 20)
or eax, (0110b shl 20) ; set MIIDevice
out dx, eax
call try_mii
test al, al
jz .base_fx
DEBUGF 1,"Using off-chip mii device\n"
ret
 
.base_fx:
; switch to register window 3
set_io 0
; check for 100BASE-FX
set_io REG_MEDIA_OPTIONS
in ax, dx ; read media option register
test al, 100b ; check 100BASE-FX
jz .aui_enable
set_io REG_INTERNAL_CONFIG
in eax, dx
and eax, not (1111b shl 20)
or eax, (0101b shl 20) ; set 100base-FX
out dx, eax
call try_link_detect
test al, al
jz .aui_enable
DEBUGF 1,"Using 100Base-FX\n"
ret
 
.aui_enable:
; switch to register window 3
set_io 0
; check for 10Mbps AUI connector
set_io REG_MEDIA_OPTIONS
in ax, dx ; read media option register
test al, 100000b ; check 10Mbps AUI connector
jz .coax_available
set_io REG_INTERNAL_CONFIG
in eax, dx
and eax, not (1111b shl 20)
or eax, (0001b shl 20) ; set 10Mbps AUI connector
out dx, eax
xor al, al ; try 10Mbps AUI connector
call try_loopback
test al, al
jz .coax_available
DEBUGF 1,"Using 10Mbps aui\n"
ret
 
.coax_available:
; switch to register window 3
set_io 0
; check for coaxial 10BASE-2 port
set_io REG_MEDIA_OPTIONS
in ax, dx ; read media option register
test al, 10000b ; check 10BASE-2
jz .set_first_available_media
 
set_io REG_INTERNAL_CONFIG
in eax, dx
and eax, not (1111b shl 20)
or eax, (0011b shl 20) ; set 10BASE-2
out dx, eax
mov al, 1
call try_loopback
test al, al
jz .set_first_available_media
DEBUGF 1,"Using 10BASE-2 port\n"
ret
 
.set_first_available_media:
DEBUGF 1,"Using the first available media\n"
 
;***************************************************************************
; Function
; set_available_media
; Description
; sets the first available media
; Parameters
; ebx - ptr to device struct
; Return value
; al - 0
; al - 1
; Destroyed registers
; eax, edx
;
;***************************************************************************
 
align 4
set_available_media:
 
DEBUGF 1,"Setting the available media\n"
; switch to register window 3
set_io 0
set_io REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+3
out dx, ax
 
set_io REG_MEDIA_OPTIONS
in ax, dx
DEBUGF 1,"available media:%x\n", al
mov cl, al
 
set_io REG_INTERNAL_CONFIG
in eax, dx
and eax, not (1111b shl 20) ; these bits hold the 'transceiver select' value
 
test cl, 10b ; baseTXAvailable
jz @f
 
DEBUGF 1,"base TX is available\n"
or eax, (100b shl 20)
if defined FORCE_FD
mov word [device.state], (1 shl 8)
else
mov word [device.mode], (1 shl 7)
end if
jmp .set_media
@@:
 
test cl, 100b ; baseFXAvailable
jz @f
 
DEBUGF 1,"base FX is available\n"
or eax, (101b shl 20)
mov word [device.state], (1 shl 10)
jmp .set_media
@@:
 
test cl, 1000000b ; miiDevice
jz @f
 
DEBUGF 1,"mii-device is available\n"
or eax, (0110b shl 20)
mov word [device.state], (1 shl 13)
jmp .set_media
@@:
 
test cl, 1000b ; 10bTAvailable
jz @f
 
DEBUGF 1,"10base-T is available\n"
.set_default:
if FORCE_FD
mov word [device.state], (1 shl 6)
else
mov word [device.state], (1 shl 5)
end if
jmp .set_media
@@:
 
test cl, 10000b ; coaxAvailable
jz @f
 
DEBUGF 1,"coax is available\n"
push eax
set_io REG_COMMAND
mov ax, (10b shl 11) ; EnableDcConverter
out dx, ax
pop eax
 
or eax, (11b shl 20)
mov word [device.state], (1 shl 12)
jmp .set_media
@@:
 
test cl, 10000b ; auiAvailable
jz .set_default
 
DEBUGF 1,"AUI is available\n"
or eax, (1 shl 20)
mov word [device.state], (1 shl 11)
 
.set_media:
set_io 0
set_io REG_INTERNAL_CONFIG
out dx, eax
 
if FORCE_FD
DEBUGF 1,"Forcing full duplex\n"
set_io REG_MAC_CONTROL
in ax, dx
or ax, 0x120
out dx, ax
end if
 
mov al, 1
ret
 
 
 
;***************************************************************************
; Function
; wake_up
; Description
; set the power state to D0
;
;***************************************************************************
 
align 4
wake_up:
 
DEBUGF 1,"Waking up NIC: "
 
; wake up - we directly do it by programming PCI
; check if the device is power management capable
stdcall PciRead32, [device.pci_bus], [device.pci_dev], PCI_REG_STATUS
 
test al, 10000b ; is there "new capabilities" linked list?
jz .device_awake
 
; search for power management register
stdcall PciRead16, [device.pci_bus], [device.pci_dev], PCI_REG_CAP_PTR
cmp al, 0x3f
jbe .device_awake
 
; traverse the list
movzx esi, al
.pm_loop:
stdcall PciRead32, [device.pci_bus], [device.pci_dev], esi
 
cmp al , 1
je .set_pm_state
 
movzx esi, ah
 
test ah , ah
jnz .pm_loop
jmp .device_awake
 
; waku up the device if necessary
.set_pm_state:
 
add esi, PCI_REG_PM_CTRL
stdcall PciRead32, [device.pci_bus], [device.pci_dev], esi
test al, 3
jz .device_awake
and al, not 11b ; set state to D0
stdcall PciWrite32, [device.pci_bus], [device.pci_dev], esi, eax
 
.device_awake:
DEBUGF 1,"Device is awake\n"
 
ret
 
 
 
 
;***************************************************************************
; Function
; write_eeprom
; Description
; reads eeprom
; Note : the caller must switch to the register window 0
; before calling this function
; Parameters:
; ax - register to be read (only the first 63 words can be read)
; cx - value to be read into the register
; Return value:
; ax - word read
; Destroyed registers
; ax, ebx, edx
;
;***************************************************************************
; align 4
;write_eeprom:
; mov edx, [io_addr]
; add edx, REG_EEPROM_COMMAND
; cmp ah, 11b
; ja .finish ; address may have a value of maximal 1023
; shl ax, 2
; shr al, 2
; push eax
;; wait for busy
; mov ebx, 0xffff
;@@:
; in ax, dx
; test ah, 0x80
; jz .write_enable
; dec ebx
; jns @r
;; write enable
;.write_enable:
; xor eax, eax
; mov eax, (11b shl 4)
; out dx, ax
;; wait for busy
; mov ebx, 0xffff
;@@:
; in ax, dx
; test ah, 0x80
; jz .erase_loop
; dec ebx
; jns @r
;.erase_loop:
; pop eax
; push eax
; or ax, (11b shl 6) ; erase register
; out dx, ax
; mov ebx, 0xffff
;@@:
; in ax, dx
; test ah, 0x80
; jz .write_reg
; dec ebx
; jns @r
;.write_reg:
; add edx, REG_EEPROM_DATA-REG_EEPROM_COMMAND
; mov eax, ecx
; out dx, ax
;; write enable
; add edx, REG_EEPROM_COMMAND-REG_EEPROM_DATA
; xor eax, eax
; mov eax, (11b shl 4)
; out dx, ax
; wait for busy
; mov ebx, 0xffff
;@@:
; in ax, dx
; test ah, 0x80
; jz .issue_write_reg
; dec ebx
; jns @r
;.issue_write_reg:
; pop eax
; or ax, 01b shl 6
; out dx, ax
;.finish:
; ret
 
 
;***************************************************************************
; Function
; read_eeprom
; Description
; reads eeprom
; Parameters:
; ax - register to be read (only the first 63 words can be read)
; ebx = driver structure
; Return value:
; ax - word read
; Destroyed registers
; ax, ebx, edx
;
;***************************************************************************
 
align 4
read_eeprom:
 
DEBUGF 1,"Reading from eeprom.. "
 
push eax
; switch to register window 0
set_io 0
set_io REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+0
out dx, ax
pop eax
and ax, 111111b ; take only the first 6 bits into account
movzx esi, [device.ver_id]
 
test word [esi*4+hw_versions+2], EEPROM_8BIT
jz @f
add ax, 0x230 ; hardware constant
jmp .read
@@:
 
add ax, EEPROM_CMD_READ
test word [esi*4+hw_versions+2], EEPROM_OFFSET
jz .read
add ax, 0x30
.read:
 
set_io REG_EEPROM_COMMAND
out dx, ax
mov ecx, 0xffff ; duration of about 162 us ;-)
.wait_for_reading:
in ax, dx
test ah, 0x80 ; check bit eepromBusy
jz .read_data
loop .wait_for_reading
.read_data:
set_io REG_EEPROM_DATA
in ax, dx
 
DEBUGF 1,"ok!\n"
 
ret
 
;***************************************************************************
; Function
; mdio_sync
; Description
; initial synchronization
; Parameters
; ebp - io_addr
; Return value
; Destroyed registers
; ax, edx, cl
;
;***************************************************************************
 
align 4
mdio_sync:
 
DEBUGF 1,"syncing mdio\n"
 
; switch to register window 4
set_io 0
set_io REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+4
out dx, ax
cmp [device.preamble], 0
je .no_preamble
; send 32 logic ones
set_io REG_PHYSICAL_MGMT
mov ecx, 31
.loop:
mov ax, (1 shl BIT_MGMT_DATA) or (1 shl BIT_MGMT_DIR)
out dx, ax
in ax, dx ; delay
mov ax, (1 shl BIT_MGMT_DATA) or (1 shl BIT_MGMT_DIR) or (1 shl BIT_MGMT_CLK)
out dx, ax
in ax, dx ; delay
loop .loop
.no_preamble:
 
ret
 
;***************************************************************************
; Function
; mdio_read
; Description
; read MII register
; see page 16 in D83840A.pdf
; Parameters
; ah - PHY addr
; al - register addr
; ebx = device structure
; Return value
; ax - register read
;
;***************************************************************************
 
align 4
mdio_read:
 
DEBUGF 1,"Reading MII registers\n"
 
push eax
call mdio_sync ; returns with window #4
pop eax
set_io 0
set_io REG_PHYSICAL_MGMT
shl al, 3
shr ax, 3
and ax, not MII_CMD_MASK
or ax, MII_CMD_READ
 
mov esi, eax
mov ecx, 13
.cmd_loop:
mov ax, (1 shl BIT_MGMT_DIR) ; write mii
bt esi, ecx
jnc .zero_bit
or al, (1 shl BIT_MGMT_DATA)
 
.zero_bit:
out dx, ax
push ax
in ax, dx ; delay
pop ax
or al, (1 shl BIT_MGMT_CLK) ; write
out dx, ax
in ax, dx ; delay
loop .cmd_loop
 
; read data (18 bits with the two transition bits)
mov ecx, 17
xor esi, esi
.read_loop:
shl esi, 1
xor eax, eax ; read comand
out dx, ax
in ax, dx ; delay
in ax, dx
test al, (1 shl BIT_MGMT_DATA)
jz .dont_set
inc esi
.dont_set:
mov ax, (1 shl BIT_MGMT_CLK)
out dx, ax
in ax, dx ; delay
loop .read_loop
mov eax, esi
 
ret
 
 
 
;***************************************************************************
; Function
; mdio_write
; Description
; write MII register
; see page 16 in D83840A.pdf
; Parameters
; ah - PHY addr
; al - register addr
; si - word to be written
; Return value
; ax - register read
;
;***************************************************************************
 
align 4
mdio_write:
 
DEBUGF 1,"Writing MII registers\n"
 
push eax
call mdio_sync
pop eax
set_io 0
set_io REG_PHYSICAL_MGMT
shl al, 3
shr ax, 3
and ax, not MII_CMD_MASK
or ax, MII_CMD_WRITE
shl eax, 2
or eax, 10b ; transition bits
shl eax, 16
mov ax, si
mov esi, eax
mov ecx, 31
 
.cmd_loop:
mov ax, (1 shl BIT_MGMT_DIR) ; write mii
bt esi, ecx
jnc @f
or al, (1 shl BIT_MGMT_DATA)
@@:
out dx, ax
push eax
in ax, dx ; delay
pop eax
or al, (1 shl BIT_MGMT_CLK) ; write
out dx, ax
in ax, dx ; delay
loop .cmd_loop
 
ret
 
 
;***************************************************************************
; Function
; check_tx_status
; Description
; Checks TxStatus queue.
; Return value
; al - 0 no error was found
; al - 1 error was found TxReset was needed
; Destroyed registers
; eax, ecx, edx, ebp
;
;***************************************************************************
 
align 4
check_tx_status:
 
DEBUGF 1,"Checking TX status\n"
 
; clear TxStatus queue
set_io 0
set_io REG_TX_STATUS
mov ecx, 31 ; max number of queue entries
 
.tx_status_loop:
in al, dx
test al, al
jz .finish ; no error
test al, 0x3f
jnz .error
.no_error_found:
; clear current TxStatus entry which advances the next one
xor al, al
out dx, al
loop .tx_status_loop
 
.finish:
 
ret
 
.error:
call tx_reset
ret
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Transmit (vortex) ;;
;; ;;
;; In: buffer pointer in [esp+4] ;;
;; size of buffer in [esp+8] ;;
;; pointer to device structure in ebx ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
vortex_transmit:
 
DEBUGF 1,"Sending packet (vortex)\n"
 
cmp dword [esp+8], MAX_ETH_FRAME_SIZE
ja .finish ; packet is too long
 
call check_tx_status
 
; switch to register window 7
set_io 0
set_io REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+7
out dx, ax
; check for master operation in progress
set_io REG_MASTER_STATUS
in ax, dx
test ah, 0x80
jnz .finish ; no DMA for sending
; program frame address to be sent
set_io REG_MASTER_ADDRESS
mov eax, [esp+4]
call GetPgAddr
out dx, eax
; program frame length
set_io REG_MASTER_LEN
mov eax, [esp+8]
;;; and eax, not 3
out dx, ax
; start DMA Down
set_io REG_COMMAND
mov ax, (10100b shl 11) + 1 ; StartDMADown
out dx, ax
.finish:
call KernelFree
add esp, 4
ret
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Transmit (boomerang) ;;
;; ;;
;; In: buffer pointer in [esp+4] ;;
;; size of buffer in [esp+8] ;;
;; pointer to device structure in ebx ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
boomerang_transmit:
 
DEBUGF 1,"Transmitting packet, buffer:%x, size:%u\n",[esp+4],[esp+8]
mov eax, [esp+4]
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 dword [esp+8], MAX_ETH_FRAME_SIZE
ja .fail
 
call check_tx_status
 
; calculate descriptor address
mov esi, [device.prev_dpd]
DEBUGF 1,"Previous DPD: %x\n", esi
add esi, dpd.size
lea ecx, [device.dpd_buffer + (NUM_TX_DESC)*dpd.size]
cmp esi, ecx
jb @f
lea esi, [device.dpd_buffer] ; Wrap if needed
@@:
DEBUGF 1,"Found a free DPD: %x\n", esi
 
; check DnListPtr
set_io 0
set_io REG_DN_LIST_PTR
in eax, dx
; mark if Dn_List_Ptr is cleared
test eax, eax
setz [device.dn_list_ptr_cleared]
 
; finish if no more free descriptor is available - FIXME!
; cmp eax, esi
; jz .finish
 
; update statistics
inc [device.packets_tx]
mov ecx, [esp+8] ; buffer size
add dword [device.bytes_tx], ecx
adc dword [device.bytes_tx + 4], 0
 
; program DPD
and [esi+dpd.next_ptr], 0
mov eax, [esp+4] ; Tx buffer address
mov [esi+dpd.realaddr], eax
call GetPgAddr
mov [esi+dpd.frag_addr], eax
mov ecx, [esp+8] ; packet size
or ecx, 0x80000000 ; last fragment
mov [esi+dpd.frag_len], ecx
 
mov ecx, [esp+8] ; packet size
; or ecx, 0x8000 ; transmission complete notification
 
or ecx, 1 shl 31
 
; test byte [device.has_hwcksm], 0xff
; jz @f
; or ecx, (1 shl 26) ; set AddTcpChecksum
;@@:
mov [esi+dpd.frame_start_hdr], ecx
 
DEBUGF 1,"DPD: lin=%x phys=%x len=%x start hdr=%x\n", [esi+dpd.realaddr]:8, [esi+dpd.frag_addr]:8, [esi+dpd.frag_len]:8, [esi+dpd.frame_start_hdr]:8
 
; calculate physical address of dpd
mov eax, esi
GetRealAddr
cmp [device.dn_list_ptr_cleared], 0
jz .add_to_list
 
; write Dn_List_Ptr
DEBUGF 1,"DPD phys addr=%x\n", eax
set_io 0
set_io REG_DN_LIST_PTR
out dx, eax
jmp .finish
 
.add_to_list:
DEBUGF 1,"Adding To list\n"
push eax
; DnStall
set_io 0
set_io REG_COMMAND
mov ax, ((110b shl 11)+2)
out dx, ax
 
; wait for DnStall to complete
DEBUGF 1,"Waiting for DnStall\n"
mov ecx, 6000
.wait_for_stall:
in ax, dx ; read REG_INT_STATUS
test ah, 10000b
jz .dnstall_ok
dec ecx
jnz .wait_for_stall
 
.dnstall_ok:
DEBUGF 1,"DnStall ok!\n"
mov ecx, [device.prev_dpd]
mov [ecx+dpd.next_ptr], eax
 
set_io 0
set_io REG_DN_LIST_PTR
in eax, dx
test eax, eax
pop eax
jnz .dnunstall
 
; if Dn_List_Ptr has been cleared fill it up
DEBUGF 1,"DnList Ptr has been cleared\n"
out dx, eax
 
.dnunstall:
; DnUnStall
set_io 0
set_io REG_COMMAND
mov ax, ((110b shl 11)+3)
out dx, ax
 
.finish:
mov [device.prev_dpd], esi
xor eax, eax
ret 8
 
.fail:
stdcall KernelFree, [esp+4]
or eax, -1
ret 8
 
 
;---------------------------------
; Write MAC
 
align 4
write_mac:
 
DEBUGF 1,"Writing mac\n"
 
set_io 0
set_io REG_COMMAND
 
; switch to register window 2
mov ax, SELECT_REGISTER_WINDOW+2
out dx, ax
 
; write MAC addres back into the station address registers
set_io REG_STATION_ADDRESS_LO
lea esi, [device.mac]
outsw
inc dx
inc dx
outsw
inc dx
inc dx
outsw
 
 
;----------------------------
; Read MAC
 
align 4
read_mac:
 
set_io 0
set_io REG_COMMAND
 
; switch to register window 2
mov ax, SELECT_REGISTER_WINDOW+2
out dx, ax
 
; write MAC addres back into the station address registers
set_io REG_STATION_ADDRESS_LO
lea edi, [device.mac]
insw
inc dx
inc dx
insw
inc dx
inc dx
insw
 
DEBUGF 1,"%x-%x-%x-%x-%x-%x\n",[device.mac]:2,[device.mac+1]:2,[device.mac+2]:2,[device.mac+3]:2,[device.mac+4]:2,[device.mac+5]:2
 
ret
 
 
;------------------------------------
; Read MAC from eeprom
 
align 4
read_mac_eeprom: ; Tested - ok
 
DEBUGF 1,"Reading mac from eeprom\n"
 
; read MAC from eeprom
mov ecx, 3
.mac_loop:
lea ax, [EEPROM_REG_OEM_NODE_ADDR+ecx-1]
push ecx
call read_eeprom
pop ecx
xchg ah, al ; htons
mov word [device.mac+ecx*2-2], ax
loop .mac_loop
 
DEBUGF 1,"%x-%x-%x-%x-%x-%x\n",[device.mac]:2,[device.mac+1]:2,[device.mac+2]:2,[device.mac+3]:2,[device.mac+4]:2,[device.mac+5]:2
 
ret
 
 
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Vortex Interrupt handler ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
int_vortex:
 
push ebx esi edi
 
DEBUGF 1,"\n%s int\n", my_service
 
; find pointer of device wich made IRQ occur
 
mov esi, VORTEX_LIST
mov ecx, [VORTEX_DEVICES]
test ecx, ecx
jz .nothing
.nextdevice:
mov ebx, dword [esi]
 
 
set_io 0
set_io REG_INT_STATUS
in ax, dx
and ax, S_5_INTS
jnz .nothing
 
add esi, 4
 
test ax , ax
jnz .got_it
loop .nextdevice
 
.nothing:
pop edi esi ebx
xor eax, eax
 
ret
 
.got_it:
 
DEBUGF 1,"Device: %x Status: %x ",ebx,eax:4
 
test ax, RxComplete
jz .noRX
 
set_io 0
.rx_status_loop:
; examine RxStatus
set_io REG_RX_STATUS
in ax, dx
test ax, ax
jz .finish
 
test ah, 0x80 ; rxIncomplete
jnz .finish
 
test ah, 0x40
jz .check_length
 
; discard the top frame received advancing the next one
set_io REG_COMMAND
mov ax, (01000b shl 11)
out dx, ax
jmp .rx_status_loop
 
.check_length:
and eax, 0x1fff
cmp eax, MAX_ETH_PKT_SIZE
ja .discard_frame ; frame is too long discard it
 
.check_dma:
mov ecx, eax
; switch to register window 7
set_io 0
set_io REG_COMMAND
mov ax, SELECT_REGISTER_WINDOW+7
out dx, ax
; check for master operation in progress
set_io REG_MASTER_STATUS
in ax, dx
 
test ah, 0x80
jnz .finish
 
.read_frame:
; program buffer address to read in
push ecx
stdcall KernelAlloc, MAX_ETH_FRAME_SIZE
pop ecx
test eax, eax
jz .finish
 
push .discard_frame
push ecx
push eax
; zero_to_dma eax
set_io REG_MASTER_ADDRESS
out dx, eax
 
; program frame length
set_io REG_MASTER_LEN
mov ax, 1560
out dx, ax
 
; start DMA Up
set_io REG_COMMAND
mov ax, (10100b shl 11) ; StartDMAUp
out dx, ax
 
; check for master operation in progress
set_io REG_MASTER_STATUS ; TODO: use timeout and reset after timeout expired
.dma_loop:
in ax, dx
test ah, 0x80
jnz .dma_loop
 
; registrate the received packet to kernel
jmp Eth_input
 
; discard the top frame received
.discard_frame:
set_io 0
set_io REG_COMMAND
mov ax, (01000b shl 11)
out dx, ax
 
.finish:
 
 
.noRX:
 
test ax, DMADone
jz .noDMA
 
push ax
 
set_io 0
set_io 12
in ax, dx
test ax, 0x1000
jz .nodmaclear
 
mov ax, 0x1000
out dx, ax
 
.nodmaclear:
 
pop ax
 
DEBUGF 1, "DMA Done!\n", cx
 
 
 
.noDMA:
 
 
 
.ACK:
set_io 0
set_io REG_COMMAND
mov ax, AckIntr + IntReq + IntLatch
out dx, ax
 
pop edi esi ebx
 
ret
 
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Boomerang Interrupt handler ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
int_boomerang:
 
push ebx esi edi
 
DEBUGF 1,"\n%s int\n", my_service
 
; find pointer of device wich made IRQ occur
 
mov ecx, [BOOMERANG_DEVICES]
test ecx, ecx
jz .nothing
mov esi, BOOMERANG_LIST
.nextdevice:
mov ebx, [esi]
 
set_io 0
set_io REG_INT_STATUS
in ax, dx
test ax, ax
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 ", ebx, ax
push ax
 
; disable all INTS
 
set_io REG_COMMAND
mov ax, SetIntrEnb
out dx, ax
 
;--------------------------------------------------------------------------
test word[esp], UpComplete
jz .noRX
 
push ebx
 
.receive:
DEBUGF 1,"UpComplete\n"
 
; check if packet is uploaded
mov esi, [device.curr_upd]
test byte [esi+upd.pkt_status+1], 0x80 ; upPktComplete
jz .finish
DEBUGF 1, "Current upd: %x\n", esi
; packet is uploaded check for any error
.check_error:
test byte [esi+upd.pkt_status+1], 0x40 ; upError
jz .copy_packet_length
DEBUGF 1,"Error in packet\n"
and [esi+upd.pkt_status], 0 ; mark packet as read
jmp .finish
.copy_packet_length:
mov ecx, [esi+upd.pkt_status]
and ecx, 0x1fff
 
; cmp ecx, MAX_ETH_PKT_SIZE
; jbe .copy_packet
; and [esi+upd.pkt_status], 0
; jmp .finish
; .copy_packet:
 
DEBUGF 1, "Received %u bytes in buffer %x\n", ecx, [esi+upd.realaddr]:8
 
push dword .loop ;.finish
push ecx
push [esi+upd.realaddr]
 
; update statistics
inc [device.packets_rx]
add dword [device.bytes_rx], ecx
adc dword [device.bytes_rx + 4], 0
 
; update UPD (Alloc new buffer for next packet)
stdcall KernelAlloc, MAX_ETH_FRAME_SIZE
mov [esi + upd.realaddr], eax
GetRealAddr
mov [esi + upd.frag_addr], eax
and [esi + upd.pkt_status], 0
mov [esi + upd.frag_len], MAX_ETH_FRAME_SIZE or (1 shl 31)
 
; Update UPD pointer
add esi, upd.size
lea ecx, [device.upd_buffer+(NUM_RX_DESC)*upd.size]
cmp esi, ecx
jb @f
lea esi, [device.upd_buffer]
@@:
mov [device.curr_upd], esi
DEBUGF 1, "Next upd: %x\n", esi
 
jmp Eth_input
.loop:
 
mov ebx, [esp]
jmp .receive
 
.finish:
pop ebx
 
; check if the NIC is in the upStall state
set_io 0
set_io REG_UP_PKT_STATUS
in eax, dx
test ah, 0x20 ; UpStalled
jz .noUpUnStall
 
DEBUGF 1, "upUnStalling\n"
; issue upUnStall command
set_io REG_COMMAND
mov ax, ((11b shl 12)+1) ; upUnStall
out dx, ax
 
;;;; FIXME: make upunstall work
 
.noUpUnStall:
.noRX:
test word[esp], DownComplete
jz .noTX
DEBUGF 1, "Downcomplete!\n"
 
mov ecx, NUM_TX_DESC
lea esi, [device.dpd_buffer]
.txloop:
test [esi+dpd.frame_start_hdr], 1 shl 31
jz .maybenext
 
and [esi+dpd.frame_start_hdr], 0
push ecx
stdcall KernelFree, [esi+dpd.realaddr]
pop ecx
 
.maybenext:
add esi, dpd.size
dec ecx
jnz .txloop
 
.noTX:
pop ax
 
set_io 0
set_io REG_COMMAND
or ax, AckIntr
out dx, ax
 
set_io REG_INT_STATUS
in ax, dx
test ax, S_5_INTS
jnz .got_it
 
;re-enable ints
set_io REG_COMMAND
mov ax, SetIntrEnb + S_5_INTS
out dx, ax
 
pop edi esi ebx
 
ret
 
 
 
 
; End of code
align 4 ; Place all initialised data here
 
macro strtbl name, [string]
{
common
label name dword
forward
local label
dd label
forward
label db string, 0
}
 
VORTEX_DEVICES dd 0
BOOMERANG_DEVICES dd 0
version dd (DRIVER_VERSION shl 16) or (API_VERSION and 0xFFFF)
my_service db '3C59X',0 ; max 16 chars include zero
 
 
strtbl link_str, \
"No valid link type detected", \
"10BASE-T half duplex", \
"10BASE-T full-duplex", \
"100BASE-TX half duplex", \
"100BASE-TX full duplex", \
"100BASE-T4", \
"100BASE-FX", \
"10Mbps AUI", \
"10Mbps COAX (BNC)", \
"miiDevice - not supported"
 
strtbl hw_str, \
"3c590 Vortex 10Mbps", \
"3c592 EISA 10Mbps Demon/Vortex", \
"3c597 EISA Fast Demon/Vortex", \
"3c595 Vortex 100baseTx", \
"3c595 Vortex 100baseT4", \
"3c595 Vortex 100base-MII", \
"3c900 Boomerang 10baseT", \
"3c900 Boomerang 10Mbps Combo", \
"3c900 Cyclone 10Mbps TPO", \
"3c900 Cyclone 10Mbps Combo", \
"3c900 Cyclone 10Mbps TPC", \
"3c900B-FL Cyclone 10base-FL", \
"3c905 Boomerang 100baseTx", \
"3c905 Boomerang 100baseT4", \
"3c905B Cyclone 100baseTx", \
"3c905B Cyclone 10/100/BNC", \
"3c905B-FX Cyclone 100baseFx", \
"3c905C Tornado", \
"3c980 Cyclone", \
"3c982 Dual Port Server Cyclone", \
"3cSOHO100-TX Hurricane", \
"3c555 Laptop Hurricane", \
"3c556 Laptop Tornado", \
"3c556B Laptop Hurricane", \
"3c575 [Megahertz] 10/100 LAN CardBus", \
"3c575 Boomerang CardBus", \
"3CCFE575BT Cyclone CardBus", \
"3CCFE575CT Tornado CardBus", \
"3CCFE656 Cyclone CardBus", \
"3CCFEM656B Cyclone+Winmodem CardBus", \
"3CXFEM656C Tornado+Winmodem CardBus", \
"3c450 HomePNA Tornado", \
"3c920 Tornado", \
"3c982 Hydra Dual Port A", \
"3c982 Hydra Dual Port B", \
"3c905B-T4", \
"3c920B-EMB-WNM Tornado"
 
 
 
align 4
hw_versions:
dw 0x5900, IS_VORTEX ; 3c590 Vortex 10Mbps
dw 0x5920, IS_VORTEX ; 3c592 EISA 10Mbps Demon/Vortex
dw 0x5970, IS_VORTEX ; 3c597 EISA Fast Demon/Vortex
dw 0x5950, IS_VORTEX ; 3c595 Vortex 100baseTx
dw 0x5951, IS_VORTEX ; 3c595 Vortex 100baseT4
dw 0x5952, IS_VORTEX ; 3c595 Vortex 100base-MII
dw 0x9000, IS_BOOMERANG ; 3c900 Boomerang 10baseT
dw 0x9001, IS_BOOMERANG ; 3c900 Boomerang 10Mbps Combo
dw 0x9004, IS_CYCLONE or HAS_NWAY or HAS_HWCKSM ; 3c900 Cyclone 10Mbps TPO
dw 0x9005, IS_CYCLONE or HAS_HWCKSM ; 3c900 Cyclone 10Mbps Combo
dw 0x9006, IS_CYCLONE or HAS_HWCKSM ; 3c900 Cyclone 10Mbps TPC
dw 0x900A, IS_CYCLONE or HAS_HWCKSM ; 3c900B-FL Cyclone 10base-FL
dw 0x9050, IS_BOOMERANG or HAS_MII ; 3c905 Boomerang 100baseTx
dw 0x9051, IS_BOOMERANG or HAS_MII ; 3c905 Boomerang 100baseT4
dw 0x9055, IS_CYCLONE or HAS_NWAY or HAS_HWCKSM or EXTRA_PREAMBLE ; 3c905B Cyclone 100baseTx
dw 0x9058, IS_CYCLONE or HAS_NWAY or HAS_HWCKSM ; 3c905B Cyclone 10/100/BNC
dw 0x905A, IS_CYCLONE or HAS_HWCKSM ; 3c905B-FX Cyclone 100baseFx
dw 0x9200, IS_TORNADO or HAS_NWAY or HAS_HWCKSM ; 3c905C Tornado
dw 0x9800, IS_CYCLONE or HAS_NWAY or HAS_HWCKSM ; 3c980 Cyclone
dw 0x9805, IS_TORNADO or HAS_NWAY or HAS_HWCKSM ; 3c982 Dual Port Server Cyclone
dw 0x7646, IS_CYCLONE or HAS_NWAY or HAS_HWCKSM ; 3cSOHO100-TX Hurricane
dw 0x5055, IS_CYCLONE or EEPROM_8BIT or HAS_HWCKSM ; 3c555 Laptop Hurricane
dw 0x6055, IS_TORNADO or HAS_NWAY or EEPROM_8BIT or HAS_CB_FNS or INVERT_MII_PWR or HAS_HWCKSM ; 3c556 Laptop Tornado
dw 0x6056, IS_TORNADO or HAS_NWAY or EEPROM_OFFSET or HAS_CB_FNS or INVERT_MII_PWR or HAS_HWCKSM ; 3c556B Laptop Hurricane
dw 0x5b57, IS_BOOMERANG or HAS_MII or EEPROM_8BIT ; 3c575 [Megahertz] 10/100 LAN CardBus
dw 0x5057, IS_BOOMERANG or HAS_MII or EEPROM_8BIT ; 3c575 Boomerang CardBus
dw 0x5157, IS_CYCLONE or HAS_NWAY or HAS_CB_FNS or EEPROM_8BIT or INVERT_LED_PWR or HAS_HWCKSM ; 3CCFE575BT Cyclone CardBus
dw 0x5257, IS_TORNADO or HAS_NWAY or HAS_CB_FNS or EEPROM_8BIT or INVERT_MII_PWR or MAX_COLLISION_RESET or HAS_HWCKSM ; 3CCFE575CT Tornado CardBus
dw 0x6560, IS_CYCLONE or HAS_NWAY or HAS_CB_FNS or EEPROM_8BIT or INVERT_MII_PWR or INVERT_LED_PWR or HAS_HWCKSM ; 3CCFE656 Cyclone CardBus
dw 0x6562, IS_CYCLONE or HAS_NWAY or HAS_CB_FNS or EEPROM_8BIT or INVERT_MII_PWR or INVERT_LED_PWR or HAS_HWCKSM ; 3CCFEM656B Cyclone+Winmodem CardBus
dw 0x6564, IS_TORNADO or HAS_NWAY or HAS_CB_FNS or EEPROM_8BIT or INVERT_MII_PWR or MAX_COLLISION_RESET or HAS_HWCKSM ; 3CXFEM656C Tornado+Winmodem CardBus
dw 0x4500, IS_TORNADO or HAS_NWAY or HAS_HWCKSM ; 3c450 HomePNA Tornado
dw 0x9201, IS_TORNADO or HAS_NWAY or HAS_HWCKSM ; 3c920 Tornado
dw 0x1201, IS_TORNADO or HAS_HWCKSM or HAS_NWAY ; 3c982 Hydra Dual Port A
dw 0x1202, IS_TORNADO or HAS_HWCKSM or HAS_NWAY ; 3c982 Hydra Dual Port B
dw 0x9056, IS_CYCLONE or HAS_NWAY or HAS_HWCKSM or EXTRA_PREAMBLE ; 3c905B-T4
dw 0x9210, IS_TORNADO or HAS_NWAY or HAS_HWCKSM ; 3c920B-EMB-WNM Tornado
HW_VERSIONS_SIZE = $ - hw_versions
 
include_debug_strings ; All data wich FDO uses will be included here
 
section '.data' data readable writable align 16 ; place all uninitialized data place here
 
VORTEX_LIST rd MAX_DEVICES ; This list contains all pointers to device structures the driver is handling
BOOMERANG_LIST rd MAX_DEVICES
 
 
 
 
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
/drivers/ethernet/R6040.asm
0,0 → 1,1120
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; R6040 driver for KolibriOS ;;
;; ;;
;; based on R6040.c from linux ;;
;; ;;
;; Written by Asper (asper.85@mail.ru) ;;
;; and hidnplayr (hidnplayr@gmail.com) ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
format MS COFF
 
API_VERSION = 0x01000100
DRIVER_VERSION = 5
 
MAX_DEVICES = 16
 
DEBUG = 1
__DEBUG__ = 1
__DEBUG_LEVEL__ = 2
 
W_MAX_TIMEOUT = 0x0FFF ; max time out delay time
 
TX_TIMEOUT = 6000 ; Time before concluding the transmitter is hung, in ms
 
TX_RING_SIZE = 4 ; RING sizes must be a power of 2
RX_RING_SIZE = 4
 
RX_BUF_LEN_IDX = 3 ; 0==8K, 1==16K, 2==32K, 3==64K
 
; Threshold is bytes transferred to chip before transmission starts.
 
TX_FIFO_THRESH = 256 ; In bytes, rounded down to 32 byte units.
 
; The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024.
 
RX_FIFO_THRESH = 4 ; Rx buffer level before first PCI xfer.
RX_DMA_BURST = 4 ; Maximum PCI burst, '4' is 256 bytes
TX_DMA_BURST = 4
 
 
 
include '../proc32.inc'
include '../imports.inc'
include '../fdo.inc'
include '../netdrv.inc'
 
public START
public service_proc
public version
 
; Operational parameters that usually are not changed.
 
PHY1_ADDR = 1 ;For MAC1
PHY2_ADDR = 3 ;For MAC2
PHY_MODE = 0x3100 ;PHY CHIP Register 0
PHY_CAP = 0x01E1 ;PHY CHIP Register 4
 
;**************************************************************************
; RDC R6040 Register Definitions
;**************************************************************************
MCR0 = 0x00 ;Control register 0
MCR1 = 0x01 ;Control register 1
MAC_RST = 0x0001 ;Reset the MAC
MBCR = 0x08 ;Bus control
MT_ICR = 0x0C ;TX interrupt control
MR_ICR = 0x10 ;RX interrupt control
MTPR = 0x14 ;TX poll command register
MR_BSR = 0x18 ;RX buffer size
MR_DCR = 0x1A ;RX descriptor control
MLSR = 0x1C ;Last status
MMDIO = 0x20 ;MDIO control register
MDIO_WRITE = 0x4000 ;MDIO write
MDIO_READ = 0x2000 ;MDIO read
MMRD = 0x24 ;MDIO read data register
MMWD = 0x28 ;MDIO write data register
MTD_SA0 = 0x2C ;TX descriptor start address 0
MTD_SA1 = 0x30 ;TX descriptor start address 1
MRD_SA0 = 0x34 ;RX descriptor start address 0
MRD_SA1 = 0x38 ;RX descriptor start address 1
MISR = 0x3C ;Status register
MIER = 0x40 ;INT enable register
MSK_INT = 0x0000 ;Mask off interrupts
RX_FINISH = 0x0001 ;RX finished
RX_NO_DESC = 0x0002 ;No RX descriptor available
RX_FIFO_FULL = 0x0004 ;RX FIFO full
RX_EARLY = 0x0008 ;RX early
TX_FINISH = 0x0010 ;TX finished
TX_EARLY = 0x0080 ;TX early
EVENT_OVRFL = 0x0100 ;Event counter overflow
LINK_CHANGED = 0x0200 ;PHY link changed
ME_CISR = 0x44 ;Event counter INT status
ME_CIER = 0x48 ;Event counter INT enable
MR_CNT = 0x50 ;Successfully received packet counter
ME_CNT0 = 0x52 ;Event counter 0
ME_CNT1 = 0x54 ;Event counter 1
ME_CNT2 = 0x56 ;Event counter 2
ME_CNT3 = 0x58 ;Event counter 3
MT_CNT = 0x5A ;Successfully transmit packet counter
ME_CNT4 = 0x5C ;Event counter 4
MP_CNT = 0x5E ;Pause frame counter register
MAR0 = 0x60 ;Hash table 0
MAR1 = 0x62 ;Hash table 1
MAR2 = 0x64 ;Hash table 2
MAR3 = 0x66 ;Hash table 3
MID_0L = 0x68 ;Multicast address MID0 Low
MID_0M = 0x6A ;Multicast address MID0 Medium
MID_0H = 0x6C ;Multicast address MID0 High
MID_1L = 0x70 ;MID1 Low
MID_1M = 0x72 ;MID1 Medium
MID_1H = 0x74 ;MID1 High
MID_2L = 0x78 ;MID2 Low
MID_2M = 0x7A ;MID2 Medium
MID_2H = 0x7C ;MID2 High
MID_3L = 0x80 ;MID3 Low
MID_3M = 0x82 ;MID3 Medium
MID_3H = 0x84 ;MID3 High
PHY_CC = 0x88 ;PHY status change configuration register
PHY_ST = 0x8A ;PHY status register
MAC_SM = 0xAC ;MAC status machine
MAC_ID = 0xBE ;Identifier register
 
MAX_BUF_SIZE = 0x600 ;1536
 
MBCR_DEFAULT = 0x012A ;MAC Bus Control Register
MCAST_MAX = 3 ;Max number multicast addresses to filter
 
;Descriptor status
DSC_OWNER_MAC = 0x8000 ;MAC is the owner of this descriptor
DSC_RX_OK = 0x4000 ;RX was successfull
DSC_RX_ERR = 0x0800 ;RX PHY error
DSC_RX_ERR_DRI = 0x0400 ;RX dribble packet
DSC_RX_ERR_BUF = 0x0200 ;RX length exceeds buffer size
DSC_RX_ERR_LONG = 0x0100 ;RX length > maximum packet length
DSC_RX_ERR_RUNT = 0x0080 ;RX packet length < 64 byte
DSC_RX_ERR_CRC = 0x0040 ;RX CRC error
DSC_RX_BCAST = 0x0020 ;RX broadcast (no error)
DSC_RX_MCAST = 0x0010 ;RX multicast (no error)
DSC_RX_MCH_HIT = 0x0008 ;RX multicast hit in hash table (no error)
DSC_RX_MIDH_HIT = 0x0004 ;RX MID table hit (no error)
DSC_RX_IDX_MID_MASK = 3 ;RX mask for the index of matched MIDx
 
;PHY settings
ICPLUS_PHY_ID = 0x0243
 
RX_INTS = RX_FIFO_FULL or RX_NO_DESC or RX_FINISH
TX_INTS = TX_FINISH
INT_MASK = RX_INTS or TX_INTS
 
RX_BUF_LEN equ (8192 << RX_BUF_LEN_IDX) ; Size of the in-memory receive ring.
 
IO_SIZE = 256 ; RDC MAC I/O Size
MAX_MAC = 2 ; MAX RDC MAC
 
 
virtual at 0
x_head:
.status dw ? ;0-1
.len dw ? ;2-3
.buf dd ? ;4-7
.ndesc dd ? ;8-B
.rev1 dd ? ;C-F
.vbufp dd ? ;10-13
.vndescp dd ? ;14-17
.skb_ptr dd ? ;18-1B
.rev2 dd ? ;1C-1F
.sizeof:
end virtual
 
 
virtual at ebx
 
device:
 
ETH_DEVICE
 
.io_addr dd ?
 
.cur_rx dw ?
.cur_tx dw ?
.last_tx dw ?
.phy_addr dw ?
.phy_mode dw ?
.mcr0 dw ?
.mcr1 dw ?
.switch_sig dw ?
 
.pci_bus dd ?
.pci_dev dd ?
.irq_line db ?
 
rb 3 ; dword alignment
 
.tx_ring: rb (((x_head.sizeof*TX_RING_SIZE)+32) and 0xfffffff0)
.rx_ring: rb (((x_head.sizeof*RX_RING_SIZE)+32) and 0xfffffff0)
 
.size = $ - device
 
end virtual
 
 
 
section '.flat' code readable align 16
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; proc START ;;
;; ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
proc START stdcall, state:dword
 
cmp [state], 1
jne .exit
 
.entry:
 
DEBUGF 2,"Loading %s driver\n", my_service
stdcall RegService, my_service, service_proc
ret
 
.fail:
.exit:
xor eax, eax
ret
 
endp
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; proc SERVICE_PROC ;;
;; ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
proc service_proc stdcall, ioctl:dword
 
mov edx, [ioctl]
mov eax, [IOCTL.io_code]
 
;------------------------------------------------------
 
cmp eax, 0 ;SRV_GETVERSION
jne @F
 
cmp [IOCTL.out_size], 4
jb .fail
mov eax, [IOCTL.output]
mov [eax], dword API_VERSION
 
xor eax, eax
ret
 
;------------------------------------------------------
@@:
cmp eax, 1 ;SRV_HOOK
jne .fail
 
cmp [IOCTL.inp_size], 3 ; Data input must be at least 3 bytes
jb .fail
 
mov eax, [IOCTL.input]
cmp byte [eax], 1 ; 1 means device number and bus number (pci) are given
jne .fail ; other types arent supported for this card yet
 
; check if the device is already listed
 
mov esi, device_list
mov ecx, [devices]
test ecx, ecx
jz .firstdevice
 
; mov eax, [IOCTL.input] ; get the pci bus and device numbers
mov ax , [eax+1] ;
.nextdevice:
mov ebx, [esi]
cmp al, byte[device.pci_bus]
jne @f
cmp ah, byte[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, device.size, .fail ; Allocate the buffer for device structure
 
; Fill in the direct call addresses into the struct
 
mov [device.reset], reset
mov [device.transmit], transmit
mov [device.unload], unload
mov [device.name], my_service
 
; save the pci bus and device numbers
 
mov eax, [IOCTL.input]
movzx ecx, byte[eax+1]
mov [device.pci_bus], ecx
movzx ecx, byte[eax+2]
mov [device.pci_dev], ecx
 
; Now, it's time to find the base io addres of the PCI device
 
PCI_find_io
 
; We've found the io address, find IRQ now
 
PCI_find_irq
 
DEBUGF 1,"Hooking into device, dev:%x, bus:%x, irq:%x, addr:%x\n",\
[device.pci_dev]:1,[device.pci_bus]:1,[device.irq_line]:1,[device.io_addr]:4
 
; Ok, the eth_device structure is ready, let's probe the device
cli
 
call probe ; this function will output in eax
test eax, eax
jnz .err_sti ; If an error occured, exit
 
mov eax, [devices] ; Add the device structure to our device list
mov [device_list+4*eax], ebx ; (IRQ handler uses this list to find device)
inc [devices] ;
 
mov [device.type], NET_TYPE_ETH
call NetRegDev
sti
 
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"
call NetPtrToNum ; This kernel procedure converts a pointer to device struct in ebx
; into a device number in edi
mov eax, edi ; Application wants it in eax instead
DEBUGF 1,"Kernel says: %u\n", eax
ret
 
; If an error occured, remove all allocated data and exit (returning -1 in eax)
 
.destroy:
; todo: reset device into virgin state
 
.err_sti:
sti
 
.err:
stdcall KernelFree, ebx
 
.fail:
or eax, -1
ret
 
;------------------------------------------------------
endp
 
 
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
;; ;;
;; Actual Hardware dependent code starts here ;;
;; ;;
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
 
 
macro mdio_write reg, val {
stdcall phy_read, [device.io_addr], [device.phy_addr], reg
}
 
macro mdio_write reg, val {
stdcall phy_write, [device.io_addr], [devce.phy_addr], reg, val
}
 
 
align 4
unload:
; TODO: (in this particular order)
;
; - Stop the device
; - Detach int handler
; - Remove device from local list (RTL8139_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 RTL8139)
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
probe:
DEBUGF 2,"Probing R6040 device\n"
 
PCI_make_bus_master
 
; If PHY status change register is still set to zero
; it means the bootloader didn't initialize it
 
set_io 0
set_io PHY_CC
in ax, dx
test ax, ax
jnz @f
mov ax, 0x9F07
out dx, ax
@@:
 
call read_mac
 
; Some bootloaders/BIOSes do not initialize MAC address, warn about that
and eax, 0xFF
or eax, dword [device.mac]
test eax, eax
jnz @f
DEBUGF 2, "ERROR: MAC address not initialized!\n"
 
@@:
; Init RDC private data
mov [device.mcr0], 0x1002
;mov [private.phy_addr], 1 ; Asper: Only one network card is supported now.
mov [device.switch_sig], 0
 
; Check the vendor ID on the PHY, if 0xFFFF assume none attached
stdcall phy_read, 1, 2
cmp ax, 0xFFFF
jne @f
DEBUGF 2, "Failed to detect an attached PHY\n" ;, generating random"
mov eax, -1
ret
@@:
 
; Set MAC address
call init_mac_regs
 
; Initialize and alloc RX/TX buffers
call init_txbufs
call init_rxbufs
 
; Read the PHY ID
mov [device.phy_mode], 0x8000
stdcall phy_read, 0, 2
mov [device.switch_sig], ax
cmp ax, ICPLUS_PHY_ID
jne @f
stdcall phy_write, 29, 31, 0x175C ; Enable registers
jmp .phy_readen
@@:
 
; PHY Mode Check
movzx eax, [device.phy_addr]
stdcall phy_write, eax, 4, PHY_CAP
stdcall phy_write, eax, 0, PHY_MODE
 
if PHY_MODE = 0x3100
call phy_mode_chk
mov [device.phy_mode], ax
jmp .phy_readen
end if
 
if not (PHY_MODE and 0x0100)
mov [device.phy_mode], 0
end if
 
.phy_readen:
 
; Set duplex mode
mov ax, [device.phy_mode]
or [device.mcr0], ax
 
; improve performance (by RDC guys)
stdcall phy_read, 30, 17
or ax, 0x4000
stdcall phy_write, 30, 17, eax
 
stdcall phy_read, 30, 17
and ax, not 0x2000
stdcall phy_write, 30, 17, eax
 
stdcall phy_write, 0, 19, 0x0000
stdcall phy_write, 0, 30, 0x01F0
 
; Initialize all Mac registers
call init_mac_regs
 
 
 
align 4
reset:
 
DEBUGF 2,"Resetting R6040\n"
 
; Mask off Interrupt
xor ax, ax
set_io 0
set_io MIER
out dx, ax
 
 
; attach int handler
 
movzx eax, [device.irq_line]
DEBUGF 2,"Attaching int handler to irq %x\n", eax:1
stdcall AttachIntHandler, eax, int_handler, dword 0
test eax, eax
jnz @f
DEBUGF 2,"\nCould not attach int handler!\n"
; or eax, -1
; ret
@@:
 
 
;Reset RDC MAC
mov eax, MAC_RST
set_io 0
set_io MCR1
out dx, ax
 
mov ecx, 2048 ;limit
.read:
in ax, dx
test ax, 0x1
jnz @f
dec ecx
test ecx, ecx
jnz .read
@@:
;Reset internal state machine
mov ax, 2
set_io MAC_SM
out dx, ax
 
xor ax, ax
out dx, ax
 
mov esi, 5
stdcall Sleep
 
;MAC Bus Control Register
mov ax, MBCR_DEFAULT
set_io 0
set_io MBCR
out dx, ax
 
;Buffer Size Register
mov ax, MAX_BUF_SIZE
set_io MR_BSR
out dx, ax
 
;Write TX ring start address
lea eax, [device.tx_ring]
GetRealAddr
set_io MTD_SA0
out dx, ax
shr eax, 16
set_io MTD_SA1
out dx, ax
 
;Write RX ring start address
lea eax, [device.rx_ring]
GetRealAddr
set_io MRD_SA0
out dx, ax
shr eax, 16
set_io MRD_SA1
out dx, ax
 
;Set interrupt waiting time and packet numbers
xor ax, ax
set_io MT_ICR
out dx, ax
 
;Enable interrupts
mov ax, INT_MASK
set_io MIER
out dx, ax
 
;Enable TX and RX
mov ax, [device.mcr0]
or ax, 0x0002
set_io 0
out dx, ax
 
;Let TX poll the descriptors
;we may got called by tx_timeout which has left
;some unset tx buffers
xor ax, ax
inc ax
set_io 0
set_io MTPR
out dx, ax
 
; Set the mtu, kernel will be able to send now
mov [device.mtu], 1514
 
; Set link state to unknown
mov [device.state], ETH_LINK_UNKOWN
 
DEBUGF 1,"Reset ok\n"
xor eax, eax
ret
 
 
 
align 4
init_txbufs:
 
DEBUGF 1,"Init TxBufs\n"
 
lea esi, [device.tx_ring]
lea eax, [device.tx_ring + x_head.sizeof]
GetRealAddr
mov ecx, TX_RING_SIZE
 
.next_desc:
mov [esi + x_head.ndesc], eax
mov [esi + x_head.skb_ptr], 0
mov [esi + x_head.status], DSC_OWNER_MAC
 
add eax, x_head.sizeof
add esi, x_head.sizeof
 
dec ecx
jnz .next_desc
 
lea eax, [device.tx_ring]
GetRealAddr
mov [device.tx_ring + x_head.sizeof*(TX_RING_SIZE - 1) + x_head.ndesc], eax
 
ret
 
 
 
align 4
init_rxbufs:
 
DEBUGF 1,"Init RxBufs\n"
 
lea esi, [device.rx_ring]
lea eax, [device.rx_ring + x_head.sizeof]
GetRealAddr
mov edx, eax
mov ecx, RX_RING_SIZE
 
.next_desc:
mov [esi + x_head.ndesc], edx
 
push esi ecx
stdcall KernelAlloc, MAX_BUF_SIZE
pop ecx esi
 
mov [esi + x_head.skb_ptr], eax
GetRealAddr
mov [esi + x_head.buf], eax
mov [esi + x_head.status], DSC_OWNER_MAC
 
add edx, x_head.sizeof
add esi, x_head.sizeof
 
dec ecx
jnz .next_desc
 
; complete the ring by linking the last to the first
 
lea eax, [device.rx_ring]
GetRealAddr
mov [device.rx_ring + x_head.sizeof*(RX_RING_SIZE - 1) + x_head.ndesc], eax
 
ret
 
 
 
align 4
phy_mode_chk:
 
DEBUGF 1,"Checking PHY mode\n"
 
; PHY Link Status Check
movzx eax, [device.phy_addr]
stdcall phy_read, eax, 1
test eax, 0x4
jz .ret_0x8000
 
; PHY Chip Auto-Negotiation Status
movzx eax, [device.phy_addr]
stdcall phy_read, eax, 1
test eax, 0x0020
jnz .auto_nego
 
; Force Mode
movzx eax, [device.phy_addr]
stdcall phy_read, eax, 0
test eax, 0x100
jnz .ret_0x8000
 
.auto_nego:
; Auto Negotiation Mode
movzx eax, [device.phy_addr]
stdcall phy_read, eax, 5
mov ecx, eax
movzx eax, [device.phy_addr]
stdcall phy_read, eax, 4
and eax, ecx
test eax, 0x140
jnz .ret_0x8000
 
xor eax, eax
ret
 
.ret_0x8000:
mov eax, 0x8000
ret
 
 
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Transmit ;;
;; ;;
;; In: buffer pointer in [esp+4] ;;
;; size of buffer in [esp+8] ;;
;; pointer to device structure in ebx ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
align 4
transmit:
DEBUGF 2,"\nTransmitting packet, buffer:%x, size:%u\n", [esp+4], [esp+8]
mov eax, [esp+4]
DEBUGF 2,"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 dword [esp+8], 1514
ja .fail
cmp dword [esp+8], 60
jb .fail
 
movzx edi, [device.cur_tx]
shl edi, 5
add edi, ebx
add edi, device.tx_ring - ebx
 
DEBUGF 2,"TX buffer status: 0x%x\n", [edi + x_head.status]:4
 
test [edi + x_head.status], DSC_OWNER_MAC ; check if buffer is available
jnz .wait_to_send
 
.do_send:
 
DEBUGF 2,"Sending now\n"
 
mov eax, [esp+4]
mov [edi + x_head.skb_ptr], eax
GetRealAddr
mov [edi + x_head.buf], eax
mov ecx, [esp+8]
mov [edi + x_head.len], cx
mov [edi + x_head.status], DSC_OWNER_MAC
 
; Trigger the MAC to check the TX descriptor
mov ax, 0x01
set_io 0
set_io MTPR
out dx, ax
 
inc [device.cur_tx]
and [device.cur_tx], TX_RING_SIZE - 1
xor eax, eax
 
; Update stats
inc [device.packets_tx]
mov eax, [esp+8]
add dword [device.bytes_tx], eax
adc dword [device.bytes_tx + 4], 0
 
ret 8
 
.wait_to_send:
 
DEBUGF 2,"Waiting for TX buffer\n"
 
call GetTimerTicks ; returns in eax
lea edx, [eax + 100]
.l2:
test [edi + x_head.status], DSC_OWNER_MAC
jz .do_send
mov esi, 10
call Sleep
call GetTimerTicks
cmp edx, eax
jb .l2
 
DEBUGF 1,"Send timeout\n"
xor eax, eax
dec eax
.fail:
DEBUGF 1,"Send failed\n"
ret 8
 
 
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Interrupt handler ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
int_handler:
 
push ebx esi edi
 
DEBUGF 1,"\n%s int\n", my_service
 
; Find pointer of device wich made IRQ occur
 
mov ecx, [devices]
test ecx, ecx
jz .nothing
mov esi, device_list
.nextdevice:
mov ebx, [esi]
 
set_io 0
set_io MISR
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)
 
 
; At this point, test for all possible reasons, and handle accordingly
 
.got_it:
 
DEBUGF 1,"Device: %x Status: %x ", ebx, ax
 
push ax
 
test word [esp], RX_FINISH
jz .no_RX
 
push ebx
.more_RX:
pop ebx
 
; Find the current RX descriptor
 
movzx edx, [device.cur_rx]
shl edx, 5
lea edx, [device.rx_ring + edx]
 
; Check the descriptor status
 
mov cx, [edx + x_head.status]
test cx, DSC_OWNER_MAC
jnz .no_RX
 
DEBUGF 2,"packet status=0x%x\n", cx
 
test cx, DSC_RX_ERR ; Global error status set
jnz .no_RX
 
; Packet successfully received
 
movzx ecx, [edx + x_head.len]
and ecx, 0xFFF
sub ecx, 4 ; Do not count the CRC
 
; Update stats
add dword [device.bytes_rx], ecx
adc dword [device.bytes_rx + 4], 0
inc dword [device.packets_rx]
 
; Push packet size and pointer, kernel will need it..
 
push ebx
push .more_RX
 
push ecx
push [edx + x_head.skb_ptr]
 
DEBUGF 2,"packet ptr=0x%x\n", [edx + x_head.skb_ptr]
 
; reset the RX descriptor
 
push edx
stdcall KernelAlloc, MAX_BUF_SIZE
pop edx
mov [edx + x_head.skb_ptr], eax
GetRealAddr
mov [edx + x_head.buf], eax
mov [edx + x_head.status], DSC_OWNER_MAC
 
; Use next descriptor next time
 
inc [device.cur_rx]
and [device.cur_rx], RX_RING_SIZE - 1
 
; At last, send packet to kernel
 
jmp Eth_input
 
 
.no_RX:
 
test word [esp], TX_FINISH
jz .no_TX
 
.loop_tx:
movzx edi, [device.last_tx]
shl edi, 5
lea edi, [device.tx_ring + edi]
 
test [edi + x_head.status], DSC_OWNER_MAC
jnz .no_TX
 
cmp [edi + x_head.skb_ptr], 0
je .no_TX
 
DEBUGF 2,"Freeing buffer 0x%x\n", [edi + x_head.skb_ptr]
 
push [edi + x_head.skb_ptr]
mov [edi + x_head.skb_ptr], 0
call KernelFree
 
inc [device.last_tx]
and [device.last_tx], TX_RING_SIZE - 1
 
jmp .loop_tx
 
.no_TX:
pop ax
 
pop edi esi ebx
 
ret
 
 
 
 
align 4
init_mac_regs:
 
DEBUGF 2,"initializing MAC regs\n"
 
; MAC operation register
mov ax, 1
set_io 0
set_io MCR1
out dx, ax
; Reset MAC
mov ax, 2
set_io MAC_SM
out dx, ax
; Reset internal state machine
xor ax, ax
out dx, ax
mov esi, 5
stdcall Sleep
 
call read_mac
 
ret
 
 
 
 
; Read a word data from PHY Chip
 
align 4
proc phy_read stdcall, phy_addr:dword, reg:dword
 
DEBUGF 2,"PHY read, addr=0x%x reg=0x%x\n", [phy_addr]:8, [reg]:8
 
mov eax, [phy_addr]
shl eax, 8
add eax, [reg]
add eax, MDIO_READ
set_io 0
set_io MMDIO
out dx, ax
 
;Wait for the read bit to be cleared.
mov ecx, 2048 ;limit
.read:
in ax, dx
test ax, MDIO_READ
jz @f
dec ecx
jnz .read
@@:
 
set_io MMRD
in ax, dx
and eax, 0xFFFF
 
DEBUGF 2,"PHY read, val=0x%x\n", eax:4
 
ret
 
endp
 
 
 
 
; Write a word data to PHY Chip
 
align 4
proc phy_write stdcall, phy_addr:dword, reg:dword, val:dword
 
DEBUGF 2,"PHY write, addr=0x%x reg=0x%x val=0x%x\n", [phy_addr]:8, [reg]:8, [val]:8
 
mov eax, [val]
set_io 0
set_io MMWD
out dx, ax
 
;Write the command to the MDIO bus
 
mov eax, [phy_addr]
shl eax, 8
add eax, [reg]
add eax, MDIO_WRITE
set_io MMDIO
out dx, ax
 
;Wait for the write bit to be cleared.
mov ecx, 2048 ;limit
.write:
in ax, dx
test ax, MDIO_WRITE
jz @f
dec ecx
jnz .write
@@:
 
DEBUGF 2,"PHY write ok\n"
 
ret
endp
 
 
 
align 4
read_mac:
 
DEBUGF 2,"Reading MAC: "
 
mov cx, 3
lea edi, [device.mac]
set_io 0
set_io MID_0L
.mac:
in ax, dx
stosw
inc dx
inc dx
dec cx
jnz .mac
 
DEBUGF 2,"%x-%x-%x-%x-%x-%x\n",[edi-6]:2, [edi-5]:2, [edi-4]:2, [edi-3]:2, [edi-2]:2, [edi-1]:2
 
ret
 
 
 
 
; End of code
 
section '.data' data readable writable align 16 ; place all uninitialized data place here
align 4 ; Place all initialised data here
 
devices dd 0
version dd (DRIVER_VERSION shl 16) or (API_VERSION and 0xFFFF)
my_service db 'R6040',0 ; max 16 chars include zero
 
include_debug_strings ; All data wich FDO uses will be included here
 
device_list rd MAX_DEVICES ; This list contains all pointers to device structures the driver is handling
 
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
/drivers/ethernet/RTL8029.asm
0,0 → 1,1203
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; RTL8029/ne2000 driver for KolibriOS ;;
;; ;;
;; based on RTL8029.asm driver for menuetos ;;
;; and realtek8029.asm for SolarOS by Eugen Brasoveanu ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; with help from CleverMouse ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
format MS COFF
 
API_VERSION = 0x01000100
DRIVER_VERSION = 5
 
MAX_DEVICES = 16
 
DEBUG = 1
__DEBUG__ = 1
__DEBUG_LEVEL__ = 2
 
include '../proc32.inc'
include '../imports.inc'
include '../fdo.inc'
include '../netdrv.inc'
 
virtual at ebx
 
device:
 
ETH_DEVICE
 
.io_addr dd ?
.irq_line db ?
.pci_bus dd ?
.pci_dev dd ?
 
.flags db ?
.vendor db ?
 
.memsize db ?
.rx_start db ?
.tx_start db ?
.bmem dd ?
.rmem dd ?
 
.size = $ - device
 
end virtual
 
 
public START
public service_proc
public version
 
P0_COMMAND = 0x00
P0_PSTART = 0x01
P0_PSTOP = 0x02
P0_BOUND = 0x03
P0_TSR = 0x04
P0_TPSR = 0x04
P0_TBCR0 = 0x05
P0_TBCR1 = 0x06
P0_ISR = 0x07
P0_RSAR0 = 0x08
P0_RSAR1 = 0x09
P0_RBCR0 = 0x0A
P0_RBCR1 = 0x0B
P0_RSR = 0x0C
P0_RCR = 0x0C
P0_TCR = 0x0D
P0_DCR = 0x0E
P0_IMR = 0x0F
 
P1_COMMAND = 0x00
P1_PAR0 = 0x01
P1_PAR1 = 0x02
P1_PAR2 = 0x03
P1_PAR3 = 0x04
P1_PAR4 = 0x05
P1_PAR5 = 0x06
P1_CURR = 0x07
P1_MAR0 = 0x08
 
CMD_PS0 = 0x00 ; Page 0 select
CMD_PS1 = 0x40 ; Page 1 select
CMD_PS2 = 0x80 ; Page 2 select
CMD_RD2 = 0x20 ; Remote DMA control
CMD_RD1 = 0x10
CMD_RD0 = 0x08
CMD_TXP = 0x04 ; transmit packet
CMD_STA = 0x02 ; start
CMD_STP = 0x01 ; stop
 
CMD_RDMA_READ = 001b shl 3
CMD_RDMA_WRITE = 010b shl 3
CMD_RDMA_SEND_PACKET = 011b shl 3
CMD_RDMA_ABORT = 100b shl 3 ; really is 1xx, Abort/Complete Remote DMA
; RDMA_MASK = 111b shl 3 ; internal, mask
 
RCR_MON = 0x20 ; monitor mode
 
DCR_FT1 = 0x40
DCR_LS = 0x08 ; Loopback select
DCR_WTS = 0x01 ; Word transfer select
 
ISR_PRX = 0x01 ; successful recv
ISR_PTX = 0x02 ; successful xmit
ISR_RXE = 0x04 ; receive error
ISR_TXE = 0x08 ; transmit error
ISR_OVW = 0x10 ; Overflow
ISR_CNT = 0x20 ; Counter overflow
ISR_RDC = 0x40 ; Remote DMA complete
ISR_RST = 0x80 ; reset
 
IRQ_MASK = ISR_PRX ;+ ISR_PTX ;+ ISR_RDC + ISR_PTX + ISR_TXE
 
RSTAT_PRX = 1 shl 0 ; successful recv
RSTAT_CRC = 1 shl 1 ; CRC error
RSTAT_FAE = 1 shl 2 ; Frame alignment error
RSTAT_OVER = 1 shl 3 ; FIFO overrun
 
TXBUF_SIZE = 6
RXBUF_END = 32
PAGE_SIZE = 256
 
ETH_ZLEN = 60
ETH_FRAME_LEN = 1514
 
FLAG_PIO = 1 shl 0
FLAG_16BIT = 1 shl 1
 
VENDOR_NONE = 0
VENDOR_WD = 1
VENDOR_NOVELL = 2
VENDOR_3COM = 3
 
NE_ASIC = 0x10
NE_RESET = 0x0F ; Used to reset card
NE_DATA = 0x00 ; Used to read/write NIC mem
 
MEM_8k = 32
MEM_16k = 64
MEM_32k = 128
 
ISA_MAX_ADDR = 0x400
 
 
 
section '.flat' code readable align 16
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; proc START
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
proc START stdcall, state:dword
 
cmp [state], 1
jne .exit
.entry:
DEBUGF 2,"Registering %s driver\n", my_service
stdcall RegService, my_service, service_proc
ret
.fail:
.exit:
xor eax, eax
ret
 
endp
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; proc SERVICE_PROC
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
proc service_proc stdcall, ioctl:dword
 
mov edx, [ioctl]
mov eax, [IOCTL.io_code]
 
;------------------------------------------------------
;---------------
cmp eax, 0 ;SRV_GETVERSION
jne @F ;---------------
 
cmp [IOCTL.out_size], 4
jb .fail
mov eax, [IOCTL.output]
mov [eax], dword API_VERSION
 
xor eax, eax
ret
 
;------------------------------------------------------
@@: ;---------
cmp eax, 1 ;SRV_HOOK
jne @F ;---------
 
DEBUGF 2,"Checking if device is already listed..\n"
 
mov eax, [IOCTL.input]
 
cmp [IOCTL.inp_size], 3
jb .fail
cmp byte [eax], 1
je .pci
 
cmp [IOCTL.inp_size], 4
jb .fail
cmp byte [eax], 0
je .isa
 
jmp .fail
 
.pci:
 
; check if the device is already listed
 
mov esi, device_list
mov ecx, [devices]
test ecx, ecx
jz .firstdevice_pci
 
mov ax, [eax+1] ; get the pci bus and device numbers
.nextdevice:
mov ebx, [esi]
cmp al, byte[device.pci_bus]
jne @f
cmp ah, byte[device.pci_dev]
je .find_devicenum ; Device is already loaded, let's find it's device number
@@:
add esi, 4
loop .nextdevice
 
.firstdevice_pci:
call create_new_struct
 
mov eax, [IOCTL.input]
movzx ecx, byte[eax+1]
mov [device.pci_bus], ecx
movzx ecx, byte[eax+2]
mov [device.pci_dev], ecx
 
; Now, it's time to find the base io addres of the PCI device
 
PCI_find_io
 
; We've found the io address, find IRQ now
 
PCI_find_irq
 
jmp .hook
 
.isa:
 
mov esi, device_list
mov ecx, [devices]
test ecx, ecx
jz .firstdevice_isa
mov al, [eax+3]
movzx edi, word [eax+1]
.nextdevice_isa:
mov ebx, [esi]
cmp edi, [device.io_addr]
jne .maybenext
cmp al, [device.irq_line]
je find_device_num
.maybenext:
add esi, 4
loop .nextdevice_isa
 
 
 
.firstdevice_isa:
call create_new_struct
 
mov eax, [IOCTL.input]
movzx ecx, word [eax+1]
mov [device.io_addr], ecx
mov cl, [eax+3]
mov [device.irq_line], cl
 
.hook:
 
DEBUGF 2,"Hooking into device, dev:%x, bus:%x, irq:%x, addr:%x\n",\
[device.pci_dev]:1,[device.pci_bus]:1,[device.irq_line]:1,[device.io_addr]:4
 
call probe ; this function will output in eax
test eax, eax
jnz .err ; If an error occured, exit
 
mov eax, [devices]
mov [device_list+4*eax], ebx
inc [devices]
 
mov [device.type], NET_TYPE_ETH
call NetRegDev
 
cmp eax, -1
jz .err
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"
call 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
 
.err:
DEBUGF 1,"Failed, removing device structure\n"
stdcall KernelFree, ebx
 
jmp .fail
 
;------------------------------------------------------
@@:
.fail:
or eax, -1
ret
 
;------------------------------------------------------
endp
 
 
create_new_struct:
 
cmp [devices], MAX_DEVICES
jae .fail
 
allocate_and_clear ebx, device.size, .fail ; Allocate the buffer for device structure
 
mov [device.reset], reset
mov [device.transmit], transmit
mov [device.unload], unload
mov [device.name], my_service
 
ret
 
.fail:
add esp, 4 ; return to caller of 'hook'
or eax, -1
ret
 
find_device_num:
 
DEBUGF 1,"Trying to find device number of already registered device\n"
mov ebx, eax
call 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
 
 
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
;; ;;
;; Actual Hardware dependent code starts here ;;
;; ;;
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
 
 
unload: ; TODO
or eax, -1
ret
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; probe: enables the device and clears the rx buffer
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
probe:
mov [device.vendor], VENDOR_NONE
mov [device.bmem], 0
 
DEBUGF 2,"Trying 16-bit mode\n"
 
mov [device.flags], FLAG_16BIT + FLAG_PIO
mov [device.memsize], MEM_32k
mov [device.tx_start], 64
mov [device.rx_start], TXBUF_SIZE + 64
 
set_io 0
set_io P0_DCR
mov al, DCR_WTS + DCR_FT1 + DCR_LS ; word transfer select +
out dx, al
 
set_io P0_PSTART
mov al, MEM_16k
out dx, al
 
set_io P0_PSTOP
mov al, MEM_32k
out dx, al
 
mov esi, my_service
mov di, 16384
mov cx, 14
call PIO_write
 
mov si, 16384
mov cx, 14
sub esp, 16
mov edi, esp
call PIO_read
 
mov esi, esp
add esp, 16
mov edi, my_service
mov ecx, 13
repe cmpsb
je ep_set_vendor
 
DEBUGF 2,"16-bit mode failed\n"
DEBUGF 2,"Trying 8-bit mode\n"
 
mov [device.flags], FLAG_PIO
mov [device.memsize], MEM_16k
mov [device.tx_start], 32
mov [device.rx_start], TXBUF_SIZE + 32
 
set_io NE_ASIC + NE_RESET
in al, dx
out dx, al
 
in al, 0x84
 
set_io P0_COMMAND
mov al, CMD_RD2 + CMD_STP
out dx, al
 
set_io P0_RCR
mov al, RCR_MON
out dx, al
 
set_io P0_DCR
mov al, DCR_FT1 + DCR_LS
out dx, al
 
set_io P0_PSTART
mov al, MEM_8k
out dx, al
 
set_io P0_PSTOP
mov al, MEM_16k
out dx, al
 
mov esi, my_service
mov di, 8192
mov cx, 14
call PIO_write
 
mov si, 8192
mov cx, 14
sub esp, 16
mov edi, esp
call PIO_read
 
mov esi, my_service
mov edi, esp
add esp, 16
mov ecx, 13
repe cmpsb
je ep_set_vendor
 
DEBUGF 2,"This is not a valid ne2000 device!\n"
or eax, -1
ret
 
 
ep_set_vendor:
 
DEBUGF 2,"Mode ok\n"
 
cmp [device.io_addr], ISA_MAX_ADDR
jbe .isa
 
DEBUGF 2,"Card is using PCI bus\n"
 
mov [device.vendor], VENDOR_NOVELL ;;; FIXME
jmp ep_check_have_vendor
 
.isa:
DEBUGF 2,"Card is using ISA bus\n"
 
mov [device.vendor], VENDOR_NOVELL
 
ep_check_have_vendor:
 
 
mov al, [device.vendor]
cmp al, VENDOR_NONE
; je exit
 
cmp al, VENDOR_3COM
je reset
 
mov eax, [device.bmem]
mov [device.rmem], eax
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; reset: Place the chip into a virgin state
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
reset:
DEBUGF 2,"Resetting device\n"
 
; attach int handler
movzx eax, [device.irq_line]
DEBUGF 1,"Attaching int handler to irq %x\n", eax:1
stdcall AttachIntHandler, eax, int_handler, dword 0
 
; Stop card + DMA
set_io 0
; set_io P0_COMMAND
mov al, CMD_PS0 + CMD_RDMA_ABORT + CMD_STP
out dx, al
 
; initialize DCR
set_io P0_DCR
mov al, DCR_FT1 + DCR_LS
test [device.flags], FLAG_16BIT
jz @f
or al, DCR_WTS ; word transfer select
@@:
out dx, al
 
; clear remote bytes count
set_io P0_RBCR0
xor al, al
out dx, al
 
set_io P0_RBCR1
out dx, al
 
; initialize Receive configuration register (until all init is done)
set_io P0_RCR
mov al, 0x20 ; monitor mode
out dx, al
 
; transmit configuration register to monitor mode (until all ini is done)
set_io P0_TCR
mov al, 2 ; internal loopback
out dx, al
 
; clear interupt status
set_io P0_ISR
mov al, 0xff
out dx, al
 
; clear IRQ mask ;;;;; CHECKME ;;;;;
set_io P0_IMR
xor al, al
out dx, al
 
; set transmit pointer
set_io P0_TPSR
mov al, [device.tx_start]
out dx, al
 
; set pagestart pointer
set_io P0_PSTART
mov al, [device.rx_start]
out dx, al
 
; set pagestop pointer
set_io P0_PSTOP
mov al, [device.memsize]
out dx, al
 
; set boundary pointer
set_io P0_BOUND
mov al, [device.memsize]
dec al
out dx, al
 
; set curr pointer
set_io P0_COMMAND
mov al, CMD_PS1 ;+ CMD_RD2 + CMD_STP ; page 1, stop mode
out dx, al
 
set_io P1_CURR
mov al, [device.rx_start]
out dx, al
 
set_io P0_COMMAND
mov al, CMD_PS0 ;+ CMD_RD2 + CMD_STA ; go to page 0, start mode
out dx, al
 
; Read MAC address and set it to registers
call read_mac
push .macret
sub esp, 6
lea esi, [device.mac]
mov edi, esp
movsd
movsw
jmp write_mac
.macret:
 
; set IRQ mask
set_io 0
set_io P0_IMR
mov al, IRQ_MASK
out dx, al
 
; start mode
set_io P0_COMMAND
mov al, CMD_STA
out dx, al
 
; clear transmit control register
set_io P0_TCR
xor al, al ; no loopback
out dx, al
 
; set receive control register ;;;;
set_io P0_RCR
mov al, 4 ; accept broadcast
out dx, al
 
; clear packet/byte counters
xor eax, eax
lea edi, [device.bytes_tx]
mov ecx, 6
rep stosd
 
; Set the mtu, kernel will be able to send now
mov [device.mtu], ETH_FRAME_LEN
 
; Set link state to unknown
mov [device.state], ETH_LINK_UNKOWN
 
; Indicate that we have successfully reset the card
xor eax, eax
DEBUGF 2,"Done!\n"
 
ret
 
 
 
;***************************************************************************
; Function
; transmit
; buffer in [esp+4], size in [esp+8], pointer to device struct in ebx
;***************************************************************************
 
align 4
transmit:
 
mov esi, [esp + 4]
mov ecx, [esp + 8]
DEBUGF 2,"Transmitting packet, buffer:%x, size:%u\n",esi, ecx
DEBUGF 2,"To: %x-%x-%x-%x-%x-%x From: %x-%x-%x-%x-%x-%x Type:%x%x\n",\
[esi+0]:2,[esi+1]:2,[esi+2]:2,[esi+3]:2,[esi+4]:2,[esi+5]:2,[esi+6]:2,[esi+7]:2,[esi+8]:2,[esi+9]:2,[esi+10]:2,[esi+11]:2,[esi+13]:2,[esi+12]:2
 
cmp ecx, ETH_FRAME_LEN
ja .err ; packet is too long
cmp ecx, ETH_ZLEN
jb .err ; packet is too short
 
movzx edi, [device.tx_start]
shl edi, 8
push cx
call PIO_write
pop cx
 
set_io 0
; set_io P0_COMMAND
mov al, CMD_PS0 + CMD_RD2 + CMD_STA
out dx, al
 
set_io P0_TPSR
mov al, [device.tx_start]
out dx, al
 
set_io P0_TBCR0
mov al, cl
out dx, al
 
set_io P0_TBCR1
mov al, ch
out dx, al
 
set_io P0_COMMAND
mov al, CMD_PS0 + CMD_TXP + CMD_RD2 + CMD_STA
out dx, al
 
DEBUGF 2," - Packet Sent!\n"
 
inc [device.packets_tx]
mov eax, [esp + 8] ; Get packet size in eax
 
add dword [device.bytes_tx], eax
adc dword [device.bytes_tx + 4], 0
 
stdcall KernelFree, [esp+4]
xor eax, eax
ret 8
 
.err:
DEBUGF 2," - Error!\n"
 
or eax, -1
stdcall KernelFree, [esp+4]
ret 8
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Interrupt handler
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
int_handler:
 
push ebx esi edi
 
DEBUGF 1,"\n%s int\n", my_service
 
; find pointer of device wich made INT occur
 
mov ecx, [devices]
test ecx, ecx
jz .nothing
mov esi, device_list
.nextdevice:
mov ebx, [esi]
 
set_io 0
; set_io P0_COMMAND
mov al, CMD_PS0
out dx, al
 
set_io P0_ISR
in al, dx
test al, al
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:2
 
push ebx
 
test al, ISR_PRX ; packet received ok ?
jz .no_rx
 
test [device.flags], FLAG_PIO
jz .no_rx ; FIXME: Only PIO mode supported for now
 
;
 
pushd .no_rx
 
; allocate a buffer
 
stdcall KernelAlloc, ETH_FRAME_LEN
test eax, eax
jz .fail_2
pushd 0
push eax
 
; read offset for current packet from device
 
set_io 0
set_io P0_BOUND ; boundary ptr is offset to next packet we need to read.
in al, dx
inc al
 
cmp al, [device.memsize]
jb @f
mov al, [device.rx_start]
@@:
mov ch, al
 
set_io P0_COMMAND
mov al, CMD_PS1
out dx, al
 
set_io P1_CURR
in al, dx ; get current page in cl
mov cl, al
 
set_io P1_COMMAND
mov al, CMD_PS0
out dx, al
 
cmp cl, [device.memsize]
jb @f
mov cl, [device.rx_start]
@@:
 
cmp cl, ch
je .fail
 
movzx esi, ch ; we are using 256 byte pages
shl esi, 8 ; esi now holds the offset for current packet
 
; Get packet header in eax
 
sub esp, 4 ; reserve 4 bytes on stack to put packet header in
mov edi, esp
mov cx, 4
call PIO_read
 
mov ecx, [esp] ; ecx now contains packet header
 
; check if packet is ok
 
test ecx, RSTAT_PRX
jz .fail_3
 
; calculate packet length in ecx
 
shr ecx, 16
sub ecx, 4 ; CRC doesnt count as data byte
mov [esp + 4 + 4], ecx
 
; check if packet size is ok
 
cmp ecx, ETH_ZLEN
jb .fail_3
cmp ecx, ETH_FRAME_LEN
ja .fail_3
 
; update stats
 
DEBUGF 2,"Received %u bytes\n", ecx
 
add dword[device.bytes_rx], ecx
adc dword[device.bytes_rx + 4], 0
inc [device.packets_rx]
 
; update read and write pointers
 
add esi, 4
mov edi, [esp + 4]
 
; now check if we can read all data at once (if we cross the end boundary, we need to wrap back to the beginning)
 
xor eax, eax
mov ah, [device.memsize]
sub eax, esi
cmp ecx, eax ; eax = number of bytes till end of buffer, ecx = bytes we need to read
jbe .no_wrap
 
DEBUGF 2,"WRAP!\n"
 
; Read first part
 
sub ecx, eax
push ecx
mov ecx, eax
 
call PIO_read ; Read the data
 
; update pointers
 
add edi, ecx
pop ecx
 
movzx esi, [device.rx_start]
shl esi, 8
 
; now read second part (or only part)
 
.no_wrap:
call PIO_read ; Read the data
 
; update boundary pointer
 
pop eax
mov al, ah
cmp al, [device.rx_start]
jne @f
mov al, [device.memsize]
@@:
 
set_io 0
set_io P0_BOUND
dec al
out dx, al
 
; now send the data to the kernel
 
jmp Eth_input
 
.fail_3:
add esp, 4
.fail:
add esp, 8
.fail_2:
 
 
.no_rx:
pop ebx
DEBUGF 2,"done\n"
 
set_io 0
set_io P0_ISR
mov al, 0xff
out dx, al
 
pop edi esi ebx
 
ret
 
 
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Write MAC address ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
write_mac: ; in: mac on stack (6 bytes)
 
DEBUGF 1,"Writing MAC\n"
 
set_io 0
mov al, CMD_PS1; + CMD_RD2 + CMD_STP
out dx, al
 
set_io P1_PAR0
mov esi, esp
mov cx, 6
@@:
lodsb
out dx, al
inc dx
loopw @r
 
add esp, 6
 
; Notice this procedure does not ret, but continues to read_mac instead.
 
;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Read MAC address ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;
 
read_mac:
 
DEBUGF 1,"Reading MAC\n"
 
xor esi, esi
mov cx, 16
sub esp, 16
mov edi, esp
call PIO_read
 
mov esi, esp
add esp, 16
lea edi, [device.mac]
mov ecx, 6
.loop:
movsb
test [device.flags], FLAG_16BIT
jz .8bit
inc esi
.8bit:
loop .loop
 
DEBUGF 1,"MAC=%x-%x-%x-%x-%x-%x\n",\
[device.mac]:2,[device.mac+1]:2,[device.mac+2]:2,[device.mac+3]:2,[device.mac+4]:2,[device.mac+5]:2
 
ret
 
 
;***************************************************************************
;
; PIO_read
;
; Description
; Read a frame from the ethernet card via Programmed I/O
; src in si
; cnt in cx
; dst in edi
;***************************************************************************
PIO_read:
 
DEBUGF 1,"PIO Read from %x to %x, %u bytes ", si, edi, cx
 
; start DMA
set_io 0
; set_io P0_COMMAND
mov al, CMD_RD2 + CMD_STA
out dx, al
 
; set length of data we're interested in
set_io P0_RBCR0
mov al, cl
out dx, al
 
set_io P0_RBCR1
mov al, ch
out dx, al
 
; set offset of what we want to read
set_io P0_RSAR0
mov ax, si
out dx, al
 
set_io P0_RSAR1
shr ax, 8
out dx, al
 
; start DMA read
set_io P0_COMMAND
mov al, CMD_RD0 + CMD_STA
out dx, al
 
set_io NE_ASIC
 
test [device.flags], FLAG_16BIT
jz .8bits
 
DEBUGF 1,"(16-bit mode)\n"
 
shr cx, 1 ; note that if the number was odd, carry flag will be set
pushf
 
.16bits:
in ax, dx
stosw
loopw .16bits
 
inc cx
popf
jnc .done
jmp .8bits_
 
.8bits:
DEBUGF 1,"(8-bit mode)\n"
 
.8bits_:
in al, dx
stosb
loopw .8bits_
 
 
.done:
; set_io 0
; set_io P0_ISR
;
; .dmawait: ; Wait for Remote DMA Complete
; in al, dx
; test al, ISR_RDC
; jz .dmawait
; and al, not ISR_RDC
; out dx, al ; clear the bit
 
ret
 
 
 
 
;***************************************************************************
;
; PIO_write
;
; Description
; writes a frame to the ethernet card via Programmed I/O
; dst in di
; cnt in cx
; src in esi
;***************************************************************************
PIO_write:
 
DEBUGF 1,"Eth PIO Write from %x to %x, %u bytes ", esi, di, cx
 
set_io 0
; set_io P0_COMMAND
mov al, CMD_RD2 + CMD_STA
out dx, al
 
set_io P0_ISR
mov al, ISR_RDC
out dx, al
 
set_io P0_RBCR0
mov al, cl
out dx, al
 
set_io P0_RBCR1
mov al, ch
out dx, al
 
mov ax, di
set_io P0_RSAR0
out dx, al
shr ax, 8
set_io P0_RSAR1
out dx, al
 
set_io P0_COMMAND
mov al, CMD_RD1 + CMD_STA
out dx, al
 
set_io NE_ASIC
test [device.flags], FLAG_16BIT
jz .8_bit
 
DEBUGF 1,"(16-bit mode)\n"
 
shr cx, 1 ; note that if the number was odd, carry flag will be set
pushf ; save the flags for later
 
.16bit:
lodsw
out dx, ax
loopw .16bit
 
popf
jnc .done
inc cx
jmp .8_bit_
 
.8_bit:
 
DEBUGF 1,"(8-bit mode)\n"
 
.8_bit_:
lodsb
out dx, al
loopw .8_bit_
 
.done:
; set_io 0
; set_io P0_ISR
; .dmawait: ; Wait for Remote DMA Complete
; in al, dx
; test al, ISR_RDC
; jz .dmawait
; and al, not ISR_RDC
; out dx, al ; clear the bit
 
ret
 
 
 
;all initialized data place here
align 4
 
devices dd 0
version dd (DRIVER_VERSION shl 16) or (API_VERSION and 0xFFFF)
my_service db 'RTL8029/ne2000',0 ;max 16 chars include zero
 
;device_1 db 'Realtek 8029',0
;device_2 db 'Realtek 8019',0
;device_3 db 'Realtek 8019AS',0
;device_4 db 'ne2000',0
;device_5 db 'DP8390',0
 
include_debug_strings
 
section '.data' data readable writable align 16 ;place all uninitialized data place here
 
device_list rd MAX_DEVICES
 
 
 
 
 
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
/drivers/ethernet/RTL8139.asm
0,0 → 1,1131
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; Realtek 8139 driver for KolibriOS ;;
;; ;;
;; based on RTL8139.asm driver for menuetos ;;
;; and realtek8139.asm for SolarOS by Eugen Brasoveanu ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
format MS COFF
 
API_VERSION = 0x01000100
DRIVER_VERSION = 5
 
MAX_DEVICES = 16
 
RBLEN = 3 ; Receive buffer size: 0==8K 1==16k 2==32k 3==64k
NUM_TX_DESC = 4
 
DEBUG = 1
__DEBUG__ = 1
__DEBUG_LEVEL__ = 2
 
include '../proc32.inc'
include '../imports.inc'
include '../fdo.inc'
include '../netdrv.inc'
 
public START
public service_proc
public version
 
REG_IDR0 = 0x00
REG_MAR0 = 0x08 ; multicast filter register 0
REG_MAR4 = 0x0c ; multicast filter register 4
REG_TSD0 = 0x10 ; transmit status of descriptor
REG_TSAD0 = 0x20 ; transmit start address of descriptor
REG_RBSTART = 0x30 ; RxBuffer start address
REG_COMMAND = 0x37 ; command register
REG_CAPR = 0x38 ; current address of packet read (word) R/W
REG_IMR = 0x3c ; interrupt mask register
REG_ISR = 0x3e ; interrupt status register
REG_TXCONFIG = 0x40 ; transmit configuration register
REG_RXCONFIG = 0x44 ; receive configuration register 0
REG_MPC = 0x4c ; missed packet counter
REG_9346CR = 0x50 ; serial eeprom 93C46 command register
REG_CONFIG1 = 0x52 ; configuration register 1
REG_MSR = 0x58
REG_CONFIG4 = 0x5a ; configuration register 4
REG_HLTCLK = 0x5b ; undocumented halt clock register
REG_BMCR = 0x62 ; basic mode control register
REG_ANAR = 0x66 ; auto negotiation advertisement register
REG_9346CR_WE = 11b shl 6
 
BIT_RUNT = 4 ; total packet length < 64 bytes
BIT_LONG = 3 ; total packet length > 4k
BIT_CRC = 2 ; crc error occured
BIT_FAE = 1 ; frame alignment error occured
BIT_ROK = 0 ; received packet is ok
 
BIT_RST = 4 ; reset bit
BIT_RE = 3 ; receiver enabled
BIT_TE = 2 ; transmitter enabled
BUFE = 1 ; rx buffer is empty, no packet stored
 
BIT_ISR_TOK = 2 ; transmit ok
BIT_ISR_RER = 1 ; receive error interrupt
BIT_ISR_ROK = 0 ; receive ok
 
BIT_TX_MXDMA = 8 ; Max DMA burst size per Tx DMA burst
BIT_TXRR = 4 ; Tx Retry count 16+(TXRR*16)
 
BIT_RXFTH = 13 ; Rx fifo threshold
BIT_RBLEN = 11 ; Ring buffer length indicator
BIT_RX_MXDMA = 8 ; Max DMA burst size per Rx DMA burst
BIT_NOWRAP = 7 ; transfered data wrapping
BIT_9356SEL = 6 ; eeprom selector 9346/9356
BIT_AER = 5 ; accept error packets
BIT_AR = 4 ; accept runt packets
BIT_AB = 3 ; accept broadcast packets
BIT_AM = 2 ; accept multicast packets
BIT_APM = 1 ; accept physical match packets
BIT_AAP = 0 ; accept all packets
 
BIT_93C46_EEM1 = 7 ; RTL8139 eeprom operating mode1
BIT_93C46_EEM0 = 6 ; RTL8139 eeprom operating mode0
BIT_93C46_EECS = 3 ; chip select
BIT_93C46_EESK = 2 ; serial data clock
BIT_93C46_EEDI = 1 ; serial data input
BIT_93C46_EEDO = 0 ; serial data output
 
BIT_LWACT = 4 ; see REG_CONFIG1
BIT_SLEEP = 1 ; sleep bit at older chips
BIT_PWRDWN = 0 ; power down bit at older chips
BIT_PMEn = 0 ; power management enabled
 
BIT_LWPTN = 2 ; see REG_CONFIG4
 
BIT_ERTXTH = 16 ; early TX threshold
BIT_TOK = 15 ; transmit ok
BIT_OWN = 13 ; tx DMA operation is completed
 
BIT_ANE = 12 ; auto negotiation enable
 
BIT_TXFD = 8 ; 100base-T full duplex
BIT_TX = 7 ; 100base-T
BIT_10FD = 6 ; 10base-T full duplex
BIT_10 = 5 ; 10base-T
BIT_SELECTOR = 0 ; binary encoded selector CSMA/CD=00001
 
BIT_IFG1 = 25
BIT_IFG0 = 24
 
TXRR = 8 ; total retries = 16+(TXRR*16)
TX_MXDMA = 6 ; 0=16 1=32 2=64 3=128 4=256 5=512 6=1024 7=2048
ERTXTH = 8 ; in unit of 32 bytes e.g:(8*32)=256
RX_MXDMA = 7 ; 0=16 1=32 2=64 3=128 4=256 5=512 6=1024 7=unlimited
RXFTH = 7 ; 0=16 1=32 2=64 3=128 4=256 5=512 6=1024 7=no threshold
 
RX_CONFIG = (RBLEN shl BIT_RBLEN) or \
(RX_MXDMA shl BIT_RX_MXDMA) or \
(1 shl BIT_NOWRAP) or \
(RXFTH shl BIT_RXFTH) or\
(1 shl BIT_AB) or \ ; Accept broadcast packets
(1 shl BIT_APM) or \ ; Accept physical match packets
(1 shl BIT_AER) or \ ; Accept error packets
(1 shl BIT_AR) or \ ; Accept Runt packets (smaller then 64 bytes)
(1 shl BIT_AM) ; Accept multicast packets
 
RX_BUFFER_SIZE = (8192 shl RBLEN);+16
MAX_ETH_FRAME_SIZE = 1514
 
EE_93C46_REG_ETH_ID = 7 ; MAC offset
EE_93C46_READ_CMD = (6 shl 6) ; 110b + 6bit address
EE_93C56_READ_CMD = (6 shl 8) ; 110b + 8bit address
EE_93C46_CMD_LENGTH = 9 ; start bit + cmd + 6bit address
EE_93C56_CMD_LENGTH = 11 ; start bit + cmd + 8bit ddress
 
VER_RTL8139 = 1100000b
VER_RTL8139A = 1110000b
VER_RTL8139AG = 1110100b
VER_RTL8139B = 1111000b
VER_RTL8130 = VER_RTL8139B
VER_RTL8139C = 1110100b
VER_RTL8100 = 1111010b
VER_RTL8100B = 1110101b
VER_RTL8139D = VER_RTL8100B
VER_RTL8139CP = 1110110b
VER_RTL8101 = 1110111b
 
IDX_RTL8139 = 0
IDX_RTL8139A = 1
IDX_RTL8139B = 2
IDX_RTL8139C = 3
IDX_RTL8100 = 4
IDX_RTL8139D = 5
IDX_RTL8139D = 6
IDX_RTL8101 = 7
 
ISR_SERR = 1 shl 15
ISR_TIMEOUT = 1 shl 14
ISR_LENCHG = 1 shl 13
ISR_FIFOOVW = 1 shl 6
ISR_PUN = 1 shl 5
ISR_RXOVW = 1 shl 4
ISR_TER = 1 shl 3
ISR_TOK = 1 shl 2
ISR_RER = 1 shl 1
ISR_ROK = 1 shl 0
 
INTERRUPT_MASK = ISR_ROK or \
ISR_RXOVW or \
ISR_PUN or \
ISR_FIFOOVW or \
ISR_LENCHG or \
ISR_TOK or \
ISR_TER
 
TSR_OWN = 1 shl 13
TSR_TUN = 1 shl 14
TSR_TOK = 1 shl 15
 
TSR_CDH = 1 shl 28
TSR_OWC = 1 shl 29
TSR_TABT = 1 shl 30
TSR_CRS = 1 shl 31
 
 
virtual at ebx
 
device:
 
ETH_DEVICE
 
.rx_buffer dd ?
 
.rx_data_offset dd ?
.io_addr dd ?
 
.curr_tx_desc db ?
.pci_bus dd ?
.pci_dev dd ?
.irq_line db ?
.hw_ver_id db ?
 
.TX_DESC rd NUM_TX_DESC
 
.size = $ - device
 
end virtual
 
 
 
section '.flat' code readable align 16
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; proc START ;;
;; ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
proc START stdcall, state:dword
 
cmp [state], 1
jne .exit
 
.entry:
 
DEBUGF 2, "Loading %s driver\n", my_service
stdcall RegService, my_service, service_proc
ret
 
.fail:
.exit:
xor eax, eax
ret
 
endp
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; proc SERVICE_PROC ;;
;; ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
proc service_proc stdcall, ioctl:dword
 
mov edx, [ioctl]
mov eax, [IOCTL.io_code]
 
;------------------------------------------------------
 
cmp eax, 0 ;SRV_GETVERSION
jne @F
 
cmp [IOCTL.out_size], 4
jb .fail
mov eax, [IOCTL.output]
mov [eax], dword API_VERSION
 
xor eax, eax
ret
 
;------------------------------------------------------
@@:
cmp eax, 1 ;SRV_HOOK
jne .fail
 
cmp [IOCTL.inp_size], 3 ; Data input must be at least 3 bytes
jb .fail
 
mov eax, [IOCTL.input]
cmp byte [eax], 1 ; 1 means device number and bus number (pci) are given
jne .fail ; other types arent supported for this card yet
 
; check if the device is already listed
 
mov esi, device_list
mov ecx, [devices]
test ecx, ecx
jz .firstdevice
 
mov ax, [eax+1] ; get the pci bus and device numbers
.nextdevice:
mov ebx, [esi]
cmp al, byte[device.pci_bus]
jne @f
cmp ah, byte[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, device.size, .fail ; Allocate the buffer for device structure
 
; Fill in the direct call addresses into the struct
 
mov [device.reset], reset
mov [device.transmit], transmit
mov [device.unload], unload
mov [device.name], my_service
 
; save the pci bus and device numbers
 
mov eax, [IOCTL.input]
movzx ecx, byte[eax+1]
mov [device.pci_bus], ecx
movzx ecx, byte[eax+2]
mov [device.pci_dev], ecx
 
; Now, it's time to find the base io addres of the PCI device
 
PCI_find_io
 
; We've found the io address, find IRQ now
 
PCI_find_irq
 
DEBUGF 2, "Hooking into device, dev:%x, bus:%x, irq:%x, I/O addr:%x\n",\
[device.pci_dev]:1,[device.pci_bus]:1,[device.irq_line]:1,[device.io_addr]:4
 
; Allocate the receive buffer
 
stdcall CreateRingBuffer, dword (RX_BUFFER_SIZE), dword PG_SW
test eax, eax
jz .err
mov [device.rx_buffer], eax
 
; Ok, the eth_device structure is ready, let's probe the device
 
call probe ; this function will output in eax
test eax, eax
jnz .err ; If an error occured, exit
 
mov eax, [devices] ; Add the device structure to our device list
mov [device_list+4*eax], ebx ; (IRQ handler uses this list to find device)
inc [devices] ;
 
mov [device.type], NET_TYPE_ETH
call NetRegDev
 
cmp eax, -1
je .destroy
 
ret
 
; If the device was already loaded, find the device number and return it in eax
 
.find_devicenum:
DEBUGF 2, "Trying to find device number of already registered device\n"
call NetPtrToNum ; This kernel procedure converts a pointer to device struct in ebx
; into a device number in edi
mov eax, edi ; Application wants it in eax instead
DEBUGF 2, "Kernel says: %u\n", eax
ret
 
; If an error occured, remove all allocated data and exit (returning -1 in eax)
 
.destroy:
; todo: reset device into virgin state
 
.err:
stdcall KernelFree, [device.rx_buffer]
stdcall KernelFree, ebx
 
.fail:
or eax, -1
ret
 
;------------------------------------------------------
endp
 
 
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
;; ;;
;; Actual Hardware dependent code starts here ;;
;; ;;
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
 
align 4
unload:
; TODO: (in this particular order)
;
; - Stop the device
; - Detach int handler
; - Remove device from local list (RTL8139_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 RTL8139)
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
probe:
DEBUGF 2, "Probing %s device\n", my_service
 
PCI_make_bus_master
 
; get chip version
set_io 0
set_io REG_TXCONFIG + 2
in ax, dx
shr ah, 2
shr ax, 6
and al, 01111111b
 
; now find it in our array
mov ecx, HW_VER_ARRAY_SIZE-1
.chip_ver_loop:
cmp al, [hw_ver_array + ecx]
je .chip_ver_found
dec ecx
jns .chip_ver_loop
.unknown:
mov ecx, 8
.chip_ver_found:
cmp ecx, 8
ja .unknown
 
mov [device.hw_ver_id], cl
 
mov ecx, [crosslist+ecx*4]
mov [device.name], ecx
 
DEBUGF 2, "Chip version: %s\n", ecx
 
; wake up the chip
set_io 0
set_io REG_HLTCLK
mov al, 'R' ; run the clock
out dx, al
 
; unlock config and BMCR registers
set_io REG_9346CR
mov al, (1 shl BIT_93C46_EEM1) or (1 shl BIT_93C46_EEM0)
out dx, al
 
; enable power management
set_io REG_CONFIG1
in al, dx
cmp [device.hw_ver_id], IDX_RTL8139B
jae .new_chip
; wake up older chips
and al, not ((1 shl BIT_SLEEP) or (1 shl BIT_PWRDWN))
out dx, al
jmp .finish_wake_up
 
; set LWAKE pin to active high (default value).
; it is for Wake-On-LAN functionality of some motherboards.
; this signal is used to inform the motherboard to execute a wake-up process.
; only at newer chips.
.new_chip:
or al, (1 shl BIT_PMEn)
and al, not (1 shl BIT_LWACT)
out dx, al
 
set_io REG_CONFIG4
in al, dx
and al, not (1 shl BIT_LWPTN)
out dx, al
 
; lock config and BMCR registers
.finish_wake_up:
xor al, al
set_io 0
set_io REG_9346CR
out dx, al
DEBUGF 2, "done!\n"
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; reset: Set up all registers and descriptors, clear some values
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
reset:
DEBUGF 2, "Reset\n"
 
; attach int handler
movzx eax, [device.irq_line]
DEBUGF 1, "Attaching int handler to irq %x\n", eax:1
stdcall AttachIntHandler, eax, int_handler, dword 0
test eax, eax
jnz @f
DEBUGF 1, "\nCould not attach int handler!\n"
; or eax, -1
; ret
@@:
 
; reset chip
DEBUGF 1, "Resetting chip\n"
set_io 0
set_io REG_COMMAND
mov al, 1 shl BIT_RST
out dx, al
mov cx, 1000 ; wait no longer for the reset
.wait_for_reset:
in al, dx
test al, 1 shl BIT_RST
jz .reset_completed ; RST remains 1 during reset
dec cx
jns .wait_for_reset
DEBUGF 1, "Reset timeout!\n"
.reset_completed:
 
; unlock config and BMCR registers
set_io REG_9346CR
mov al, (1 shl BIT_93C46_EEM1) or (1 shl BIT_93C46_EEM0)
out dx, al
 
; initialize multicast registers (no filtering)
mov eax, 0xffffffff
set_io REG_MAR0
out dx, eax
set_io REG_MAR4
out dx, eax
 
; enable Rx/Tx
 
mov al, (1 shl BIT_RE) or (1 shl BIT_TE)
set_io REG_COMMAND
out dx, al
 
; Rxbuffer size, unlimited dma burst, no wrapping, no rx threshold
; accept broadcast packets, accept physical match packets
mov ax, RX_CONFIG
set_io REG_RXCONFIG
out dx, ax
 
; 1024 bytes DMA burst, total retries = 16 + 8 * 16 = 144
mov eax, (TX_MXDMA shl BIT_TX_MXDMA) or (TXRR shl BIT_TXRR) or BIT_IFG1 or BIT_IFG0
set_io REG_TXCONFIG
out dx, eax
 
; enable auto negotiation
set_io REG_BMCR
in ax, dx
or ax, (1 shl BIT_ANE)
out dx, ax
 
; set auto negotiation advertisement
set_io REG_ANAR
in ax, dx
or ax, (1 shl BIT_SELECTOR) or (1 shl BIT_10) or (1 shl BIT_10FD) or (1 shl BIT_TX) or (1 shl BIT_TXFD)
out dx, ax
 
; lock config and BMCR registers
xor eax, eax
set_io REG_9346CR
out dx, al
 
; init RX/TX pointers
mov [device.rx_data_offset], eax
mov [device.curr_tx_desc], al
; set_io REG_CAPR
; out dx, ax
 
; clear packet/byte counters
lea edi, [device.bytes_tx]
mov ecx, 6
rep stosd
 
; clear missing packet counter
set_io REG_MPC
out dx, eax
 
; set RxBuffer address, init RX buffer offset
mov eax, [device.rx_buffer]
mov dword[eax], 0 ; clear receive flags for first packet (really needed??)
DEBUGF 2, "RX buffer virtual addr=0x%x\n", eax
GetRealAddr
DEBUGF 2, "RX buffer real addr=0x%x\n", eax
set_io REG_RBSTART
out dx, eax
 
; Read MAC address
call read_mac
 
; enable interrupts
set_io 0
set_io REG_IMR
mov ax, INTERRUPT_MASK
out dx, ax
 
; Set the mtu, kernel will be able to send now
mov [device.mtu], 1514
 
call cable
 
; Indicate that we have successfully reset the card
xor eax, eax
ret
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Transmit ;;
;; ;;
;; In: buffer pointer in [esp+4] ;;
;; size of buffer in [esp+8] ;;
;; pointer to device structure in ebx ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
align 4
transmit:
DEBUGF 1, "\nTransmitting packet, buffer:%x, size:%u\n", [esp+4], [esp+8]
mov eax, [esp+4]
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 dword [esp+8], MAX_ETH_FRAME_SIZE
ja .fail
cmp dword [esp+8], 60
jb .fail
 
; check if we own the current discriptor
set_io 0
set_io REG_TSD0
movzx ecx, [device.curr_tx_desc]
shl ecx, 2
add edx, ecx
in eax, dx
test eax, (1 shl BIT_OWN)
jz .wait_to_send
 
.send_packet:
; get next descriptor
inc [device.curr_tx_desc]
and [device.curr_tx_desc], NUM_TX_DESC-1
 
; Update stats
inc [device.packets_tx]
mov eax, [esp+8]
add dword [device.bytes_tx], eax
adc dword [device.bytes_tx+4], 0
 
; Set the buffer address
set_io REG_TSAD0
mov eax, [esp+4]
mov [device.TX_DESC+ecx], eax
GetRealAddr
out dx, eax
 
; And the size of the buffer
set_io REG_TSD0
mov eax, [esp+8]
or eax, (ERTXTH shl BIT_ERTXTH) ; Early threshold
out dx, eax
 
DEBUGF 1, "Packet Sent!\n"
xor eax, eax
ret 8
 
.wait_to_send:
DEBUGF 1, "Waiting for timeout\n"
 
push edx
mov esi, 30
stdcall Sleep
pop edx
 
in ax, dx
test ax, (1 shl BIT_OWN)
jnz .send_packet
 
pusha
call reset ; if chip hung, reset it
popa
 
jmp .send_packet
 
.fail:
DEBUGF 1, "failed!\n"
stdcall KernelFree, [esp+4]
or eax, -1
ret 8
 
 
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Interrupt handler ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
int_handler:
 
push ebx esi edi
 
DEBUGF 1, "\n%s int\n", my_service
 
; find pointer of device wich made IRQ occur
mov ecx, [devices]
test ecx, ecx
jz .nothing
mov esi, device_list
.nextdevice:
mov ebx, [esi]
 
set_io 0
set_io REG_ISR
in ax, dx ; Get interrupt status
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, "Device: %x Status: %x\n", ebx, ax
 
;----------------------------------------------------
; Received packet ok?
 
test ax, ISR_ROK
jz @f
push ax
 
.receive:
set_io 0
set_io REG_COMMAND
in al, dx
test al, BUFE ; test if RX buffer is empty
jnz .finish
 
DEBUGF 1, "RX: "
 
mov eax, [device.rx_buffer]
add eax, [device.rx_data_offset]
test byte [eax], (1 shl BIT_ROK) ; check if packet is ok
jz .reset_rx
 
; packet is ok, copy it
movzx ecx, word [eax+2] ; packet length
sub cx, 4 ; don't copy CRC
 
; Update stats
add dword [device.bytes_rx], ecx
adc dword [device.bytes_rx + 4], 0
inc [device.packets_rx]
 
DEBUGF 1, "Received %u bytes\n", ecx
 
push ebx eax ecx
stdcall KernelAlloc, ecx ; Allocate a buffer to put packet into
pop ecx
test eax, eax ; Test if we allocated succesfully
jz .abort
 
mov edi, eax ; Where we will copy too
 
mov esi, [esp] ; The buffer we will copy from
add esi, 4 ; Dont copy CRC
 
push dword .abort ; Kernel will return to this address after EthReceiver
push ecx edi ; Save buffer pointer and size, to pass to kernel
 
.copy:
shr ecx, 1
jnc .nb
movsb
.nb:
shr ecx, 1
jnc .nw
movsw
.nw:
jz .nd
rep movsd
.nd:
 
jmp Eth_input ; Send it to kernel
 
.abort:
pop eax ebx
; update eth_data_start_offset
movzx eax, word [eax+2] ; packet length
add eax, [device.rx_data_offset]
add eax, 4+3 ; packet header is 4 bytes long + dword alignment
and eax, not 3 ; dword alignment
 
cmp eax, RX_BUFFER_SIZE
jb .no_wrap
DEBUGF 2, "Wrapping"
sub eax, RX_BUFFER_SIZE
.no_wrap:
mov [device.rx_data_offset], eax
DEBUGF 1, "New RX ptr: %d\n", eax
 
set_io 0
set_io REG_CAPR ; update 'Current Address of Packet Read register'
sub eax, 0x10 ; value 0x10 is a constant for CAPR
out dx , ax
 
jmp .receive ; check for multiple packets
 
.reset_rx:
test byte [eax], (1 shl BIT_CRC)
jz .no_crc_error
DEBUGF 2, "\nCRC error!\n"
 
.no_crc_error:
test byte [eax], (1 shl BIT_FAE)
jz .no_fae_error
DEBUGF 1, "\nFrame alignment error!\n"
 
.no_fae_error:
DEBUGF 1, "Reset RX\n"
in al, dx ; read command register
push ax
and al, not (1 shl BIT_RE) ; Clear the RE bit
out dx, al
pop ax
out dx, al ; write original command back
 
add edx, REG_RXCONFIG - REG_COMMAND ; Restore RX configuration
mov ax, RX_CONFIG
out dx, ax
 
.finish:
pop ax
 
;----------------------------------------------------
; Transmit ok / Transmit error
@@:
test ax, ISR_TOK + ISR_TER
jz @f
 
push ax
mov ecx, (NUM_TX_DESC-1)*4
.txdescloop:
set_io 0
set_io REG_TSD0
add edx, ecx
in eax, dx
 
test eax, TSR_OWN ; DMA operation completed
jz .notthisone
 
cmp [device.TX_DESC+ecx], 0
je .notthisone
 
; .notxd:
; test eax, TSR_TUN
; jz .nobun
; DEBUGF 2, "TX: FIFO Buffer underrun!\n"
;
; .nobun:
; test eax, TSR_OWC
; jz .noowc
; DEBUGF 2, "TX: OWC!\n"
;
; .noowc:
; test eax, TSR_TABT
; jz .notabt
; DEBUGF 2, "TX: TABT!\n"
;
; .notabt:
; test eax, TSR_CRS
; jz .nocsl
; DEBUGF 2, "TX: Carrier Sense Lost!\n"
;
; .nocsl:
 
DEBUGF 1, "TX OK: free buffer %x\n", [device.TX_DESC+ecx]:8
push ecx ebx
stdcall KernelFree, [device.TX_DESC+ecx]
pop ebx ecx
mov [device.TX_DESC+ecx], 0
 
.notthisone:
sub ecx, 4
ja .txdescloop
pop ax
 
;----------------------------------------------------
; Rx buffer overflow ?
@@:
test ax, ISR_RXOVW
jz @f
 
push ax
DEBUGF 2, "RX-buffer overflow!\n"
 
set_io 0
set_io REG_ISR
mov ax, ISR_FIFOOVW or ISR_RXOVW
out dx, ax
pop ax
 
;----------------------------------------------------
; Packet underrun?
@@:
test ax, ISR_PUN
jz @f
 
DEBUGF 2, "Packet underrun!\n"
 
;----------------------------------------------------
; Receive FIFO overflow ?
@@:
test ax, ISR_FIFOOVW
jz @f
 
push ax
DEBUGF 2, "RX fifo overflow!\n"
 
set_io 0
set_io REG_ISR
mov ax, ISR_FIFOOVW or ISR_RXOVW
out dx, ax
pop ax
 
;----------------------------------------------------
; Something about Cable changed ?
@@:
test ax, ISR_LENCHG
jz .fail
 
call cable
 
.fail:
DEBUGF 2, "\n"
pop edi esi ebx
xor eax, eax
inc eax
 
ret
 
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Update Cable status ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
cable:
DEBUGF 1, "Updating Cable status\n"
 
set_io 0
set_io REG_MSR
in al, dx
 
test al, 1 shl 2 ; 0 = link ok 1 = link fail
jnz .notconnected
 
test al, 1 shl 3 ; 0 = 100 Mbps 1 = 10 Mbps
jnz .10mbps
 
.100mbps:
mov [device.state], ETH_LINK_100M
call NetLinkChanged
 
ret
 
.10mbps:
mov [device.state], ETH_LINK_10M
call NetLinkChanged
 
ret
 
.notconnected:
mov [device.state], ETH_LINK_DOWN
call NetLinkChanged
 
ret
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Write MAC address ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
write_mac: ; in: mac pushed onto stack (as 3 words)
 
DEBUGF 2, "Writing MAC: "
 
; disable all in command registers
set_io 0
set_io REG_9346CR
xor eax, eax
out dx, al
 
set_io REG_IMR
xor eax, eax
out dx, ax
 
set_io REG_ISR
mov eax, -1
out dx, ax
 
; enable writing
set_io REG_9346CR
mov eax, REG_9346CR_WE
out dx, al
 
; write the mac ...
set_io REG_IDR0
pop eax
out dx, eax
 
set_io REG_IDR0+4
xor eax, eax
pop ax
out dx, eax
 
; disable writing
set_io REG_9346CR
xor eax, eax
out dx, al
 
DEBUGF 2, "ok!\n"
 
; Notice this procedure does not ret, but continues to read_mac instead.
 
 
;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Read MAC address ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;
 
read_mac:
DEBUGF 2, "Reading MAC: "
 
set_io 0
lea edi, [device.mac]
in eax, dx
stosd
add edx, 4
in ax, dx
stosw
 
DEBUGF 2, "%x-%x-%x-%x-%x-%x\n",[edi-6]:2,[edi-5]:2,[edi-4]:2,[edi-3]:2,[edi-2]:2,[edi-1]:2
 
ret
 
 
; End of code
 
section '.data' data readable writable align 16 ; place all uninitialized data place here
align 4 ; Place all initialised data here
 
devices dd 0
version dd (DRIVER_VERSION shl 16) or (API_VERSION and 0xFFFF)
my_service db 'RTL8139',0 ; max 16 chars include zero
 
device_1 db 'Realtek 8139',0
device_2 db 'Realtek 8139A',0
device_3 db 'Realtek 8139B',0
device_4 db 'Realtek 8139C',0
device_5 db 'Realtek 8100',0
device_6 db 'Realtek 8139D',0
device_7 db 'Realtek 8139CP',0
device_8 db 'Realtek 8101',0
device_unknown db 'Unknown RTL8139 clone', 0
 
crosslist:
dd device_1
dd device_2
dd device_3
dd device_4
dd device_5
dd device_6
dd device_7
dd device_8
dd device_unknown
 
hw_ver_array: ; This array is used by the probe routine to find out wich version of the RTL8139 we are working with
db VER_RTL8139
db VER_RTL8139A
db VER_RTL8139B
db VER_RTL8139C
db VER_RTL8100
db VER_RTL8139D
db VER_RTL8139CP
db VER_RTL8101
db 0
 
HW_VER_ARRAY_SIZE = $-hw_ver_array
 
include_debug_strings ; All data wich FDO uses will be included here
 
device_list rd MAX_DEVICES ; This list contains all pointers to device structures the driver is handling
 
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
/drivers/ethernet/RTL8169.asm
0,0 → 1,1324
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; RTL8169 driver for KolibriOS ;;
;; ;;
;; Copyright 2007 mike.dld, ;;
;; mike.dld@gmail.com ;;
;; ;;
;; port to net branch by hidnplayr ;;
;; ;;
;; References: ;;
;; r8169.c - linux driver (etherboot project) ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
format MS COFF
 
API_VERSION = 0x01000100
DRIVER_VERSION = 5
 
MAX_DEVICES = 16
 
DEBUG = 1
__DEBUG__ = 1
__DEBUG_LEVEL__ = 2
 
NUM_TX_DESC = 4
NUM_RX_DESC = 4
 
include '../proc32.inc'
include '../imports.inc'
include '../fdo.inc'
include '../netdrv.inc'
 
public START
public service_proc
public version
 
 
REG_MAC0 = 0x0 ; Ethernet hardware address
REG_MAR0 = 0x8 ; Multicast filter
REG_TxDescStartAddr = 0x20
REG_TxHDescStartAddr = 0x28
REG_FLASH = 0x30
REG_ERSR = 0x36
REG_ChipCmd = 0x37
REG_TxPoll = 0x38
REG_IntrMask = 0x3C
REG_IntrStatus = 0x3E
REG_TxConfig = 0x40
REG_RxConfig = 0x44
REG_RxMissed = 0x4C
REG_Cfg9346 = 0x50
REG_Config0 = 0x51
REG_Config1 = 0x52
REG_Config2 = 0x53
REG_Config3 = 0x54
REG_Config4 = 0x55
REG_Config5 = 0x56
REG_MultiIntr = 0x5C
REG_PHYAR = 0x60
REG_TBICSR = 0x64
REG_TBI_ANAR = 0x68
REG_TBI_LPAR = 0x6A
REG_PHYstatus = 0x6C
REG_RxMaxSize = 0xDA
REG_CPlusCmd = 0xE0
REG_RxDescStartAddr = 0xE4
REG_ETThReg = 0xEC
REG_FuncEvent = 0xF0
REG_FuncEventMask = 0xF4
REG_FuncPresetState = 0xF8
REG_FuncForceEvent = 0xFC
 
; InterruptStatusBits
ISB_SYSErr = 0x8000
ISB_PCSTimeout = 0x4000
ISB_SWInt = 0x0100
ISB_TxDescUnavail = 0x80
ISB_RxFIFOOver = 0x40
ISB_LinkChg = 0x20
ISB_RxOverflow = 0x10
ISB_TxErr = 0x08
ISB_TxOK = 0x04
ISB_RxErr = 0x02
ISB_RxOK = 0x01
 
; RxStatusDesc
SD_RxRES = 0x00200000
SD_RxCRC = 0x00080000
SD_RxRUNT = 0x00100000
SD_RxRWT = 0x00400000
 
; ChipCmdBits
CMD_Reset = 0x10
CMD_RxEnb = 0x08
CMD_TxEnb = 0x04
CMD_RxBufEmpty = 0x01
 
; Cfg9346Bits
CFG_9346_Lock = 0x00
CFG_9346_Unlock = 0xC0
 
; rx_mode_bits
RXM_AcceptErr = 0x20
RXM_AcceptRunt = 0x10
RXM_AcceptBroadcast = 0x08
RXM_AcceptMulticast = 0x04
RXM_AcceptMyPhys = 0x02
RXM_AcceptAllPhys = 0x01
 
; RxConfigBits
RXC_FIFOShift = 13
RXC_DMAShift = 8
 
; TxConfigBits
TXC_InterFrameGapShift = 24
TXC_DMAShift = 8 ; DMA burst value (0-7) is shift this many bits
 
; PHYstatus
PHYS_TBI_Enable = 0x80
PHYS_TxFlowCtrl = 0x40
PHYS_RxFlowCtrl = 0x20
PHYS_1000bpsF = 0x10
PHYS_100bps = 0x08
PHYS_10bps = 0x04
PHYS_LinkStatus = 0x02
PHYS_FullDup = 0x01
 
; GIGABIT_PHY_registers
PHY_CTRL_REG = 0
PHY_STAT_REG = 1
PHY_AUTO_NEGO_REG = 4
PHY_1000_CTRL_REG = 9
 
; GIGABIT_PHY_REG_BIT
PHY_Restart_Auto_Nego = 0x0200
PHY_Enable_Auto_Nego = 0x1000
 
; PHY_STAT_REG = 1
PHY_Auto_Neco_Comp = 0x0020
 
; PHY_AUTO_NEGO_REG = 4
PHY_Cap_10_Half = 0x0020
PHY_Cap_10_Full = 0x0040
PHY_Cap_100_Half = 0x0080
PHY_Cap_100_Full = 0x0100
 
; PHY_1000_CTRL_REG = 9
PHY_Cap_1000_Full = 0x0200
PHY_Cap_1000_Half = 0x0100
 
PHY_Cap_PAUSE = 0x0400
PHY_Cap_ASYM_PAUSE = 0x0800
 
PHY_Cap_Null = 0x0
 
; _MediaType
MT_10_Half = 0x01
MT_10_Full = 0x02
MT_100_Half = 0x04
MT_100_Full = 0x08
MT_1000_Full = 0x10
 
; _TBICSRBit
TBI_LinkOK = 0x02000000
 
; _DescStatusBit
DSB_OWNbit = 0x80000000
DSB_EORbit = 0x40000000
DSB_FSbit = 0x20000000
DSB_LSbit = 0x10000000
 
RX_BUF_SIZE = 1536 ; Rx Buffer size
 
; max supported gigabit ethernet frame size -- must be at least (dev->mtu+14+4)
MAX_ETH_FRAME_SIZE = 1536
 
TX_FIFO_THRESH = 256 ; In bytes
 
RX_FIFO_THRESH = 7 ; 7 means NO threshold, Rx buffer level before first PCI xfer
RX_DMA_BURST = 7 ; Maximum PCI burst, '6' is 1024
TX_DMA_BURST = 7 ; Maximum PCI burst, '6' is 1024
ETTh = 0x3F ; 0x3F means NO threshold
 
EarlyTxThld = 0x3F ; 0x3F means NO early transmit
RxPacketMaxSize = 0x0800 ; Maximum size supported is 16K-1
InterFrameGap = 0x03 ; 3 means InterFrameGap = the shortest one
 
HZ = 1000
 
RTL_MIN_IO_SIZE = 0x80
TX_TIMEOUT = (6*HZ)
 
TIMER_EXPIRE_TIME = 100
 
ETH_HDR_LEN = 14
DEFAULT_MTU = 1500
DEFAULT_RX_BUF_LEN = 1536
 
 
;ifdef JUMBO_FRAME_SUPPORT
; MAX_JUMBO_FRAME_MTU = 10000
; MAX_RX_SKBDATA_SIZE = (MAX_JUMBO_FRAME_MTU + ETH_HDR_LEN )
;else
MAX_RX_SKBDATA_SIZE = 1600
;end if
 
MCFG_METHOD_01 = 0x01
MCFG_METHOD_02 = 0x02
MCFG_METHOD_03 = 0x03
MCFG_METHOD_04 = 0x04
MCFG_METHOD_05 = 0x05
MCFG_METHOD_11 = 0x0b
MCFG_METHOD_12 = 0x0c
MCFG_METHOD_13 = 0x0d
MCFG_METHOD_14 = 0x0e
MCFG_METHOD_15 = 0x0f
 
PCFG_METHOD_1 = 0x01 ; PHY Reg 0x03 bit0-3 == 0x0000
PCFG_METHOD_2 = 0x02 ; PHY Reg 0x03 bit0-3 == 0x0001
PCFG_METHOD_3 = 0x03 ; PHY Reg 0x03 bit0-3 == 0x0002
 
virtual at 0
tx_desc:
.status dd ?
.vlan_tag dd ?
.buf_addr dq ?
.size = $
rb (NUM_TX_DESC-1)*tx_desc.size
.buf_soft_addr dd ?
end virtual
 
virtual at 0
rx_desc:
.status dd ?
.vlan_tag dd ?
.buf_addr dq ?
.size = $
rb (NUM_RX_DESC-1)*rx_desc.size
.buf_soft_addr dd ?
end virtual
 
virtual at ebx
 
device:
 
ETH_DEVICE
 
.io_addr dd ?
.pci_bus dd ?
.pci_dev dd ?
.irq_line db ?
 
rb 256-(($ - device) and 255) ; align 256
.tx_ring rb NUM_TX_DESC * tx_desc.size * 2
 
rb 256-(($ - device) and 255) ; align 256
.rx_ring rb NUM_RX_DESC * rx_desc.size * 2
 
tpc:
.mmio_addr dd ? ; memory map physical address
.chipset dd ?
.pcfg dd ?
.mcfg dd ?
.cur_rx dd ? ; Index into the Rx descriptor buffer of next Rx pkt
.cur_tx dd ? ; Index into the Tx descriptor buffer of next Rx pkt
.TxDescArrays dd ? ; Index of Tx Descriptor buffer
.RxDescArrays dd ? ; Index of Rx Descriptor buffer
.TxDescArray dd ? ; Index of 256-alignment Tx Descriptor buffer
.RxDescArray dd ? ; Index of 256-alignment Rx Descriptor buffer
 
device_size = $ - device
 
end virtual
 
intr_mask = ISB_LinkChg or ISB_RxOverflow or ISB_RxFIFOOver or ISB_TxErr or ISB_TxOK or ISB_RxErr or ISB_RxOK
rx_config = (RX_FIFO_THRESH shl RXC_FIFOShift) or (RX_DMA_BURST shl RXC_DMAShift) or 0x0000000E
 
 
macro udelay msec {
 
push esi
mov esi, msec
call Sleep
pop esi
 
}
 
macro WRITE_GMII_REG RegAddr, value {
 
set_io REG_PHYAR
if value eq ax
and eax, 0x0000ffff
or eax, 0x80000000 + (RegAddr shl 16)
else
mov eax, 0x80000000 + (RegAddr shl 16) + value
end if
out dx, eax
 
call PHY_WAIT_WRITE
}
 
macro READ_GMII_REG RegAddr {
 
local .error, .done
 
set_io REG_PHYAR
mov eax, RegAddr shl 16
out dx, eax
 
call PHY_WAIT_READ
jz .error
 
in eax, dx
and eax, 0xFFFF
jmp .done
 
.error:
or eax, -1
.done:
}
 
align 4
PHY_WAIT_READ: ; io addr must already be set to REG_PHYAR
 
udelay 1 ;;;1000
 
push ecx
mov ecx, 2000
; Check if the RTL8169 has completed writing/reading to the specified MII register
@@:
in eax, dx
test eax, 0x80000000
jnz .exit
udelay 1 ;;;100
loop @b
.exit:
pop ecx
ret
 
align 4
PHY_WAIT_WRITE: ; io addr must already be set to REG_PHYAR
 
udelay 1 ;;;1000
 
push ecx
mov ecx, 2000
; Check if the RTL8169 has completed writing/reading to the specified MII register
@@:
in eax, dx
test eax, 0x80000000
jz .exit
udelay 1 ;;;100
loop @b
.exit:
pop ecx
ret
 
 
 
section '.flat' code readable align 16
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; proc START ;;
;; ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
proc START stdcall, state:dword
 
cmp [state], 1
jne .exit
 
.entry:
 
DEBUGF 2,"Loading %s driver\n", my_service
stdcall RegService, my_service, service_proc
ret
 
.fail:
.exit:
xor eax, eax
ret
 
endp
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; proc SERVICE_PROC ;;
;; ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
proc service_proc stdcall, ioctl:dword
 
mov edx, [ioctl]
mov eax, [IOCTL.io_code]
 
;------------------------------------------------------
 
cmp eax, 0 ;SRV_GETVERSION
jne @F
 
cmp [IOCTL.out_size], 4
jb .fail
mov eax, [IOCTL.output]
mov [eax], dword API_VERSION
 
xor eax, eax
ret
 
;------------------------------------------------------
@@:
cmp eax, 1 ;SRV_HOOK
jne .fail
 
cmp [IOCTL.inp_size], 3 ; Data input must be at least 3 bytes
jb .fail
 
mov eax, [IOCTL.input]
cmp byte [eax], 1 ; 1 means device number and bus number (pci) are given
jne .fail ; other types arent supported for this card yet
 
; check if the device is already listed
 
mov esi, device_list
mov ecx, [devices]
test ecx, ecx
jz .firstdevice
 
; mov eax, [IOCTL.input] ; get the pci bus and device numbers
mov ax , [eax+1] ;
.nextdevice:
mov ebx, [esi]
cmp al, byte[device.pci_bus]
jne @f
cmp ah, byte[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, device_size, .fail ; Allocate memory to put the device structure in
 
; Fill in the direct call addresses into the struct
 
mov [device.reset], reset
mov [device.transmit], transmit
mov [device.unload], unload
mov [device.name], my_service
 
; save the pci bus and device numbers
 
mov eax, [IOCTL.input]
movzx ecx, byte[eax+1]
mov [device.pci_bus], ecx
movzx ecx, byte[eax+2]
mov [device.pci_dev], ecx
 
; Now, it's time to find the base io addres of the PCI device
 
PCI_find_io
mov [tpc.mmio_addr], eax ; CHECKME
 
; We've found the io address, find IRQ now
 
PCI_find_irq
 
DEBUGF 2,"Hooking into device, dev:%x, bus:%x, irq:%x, addr:%x\n",\
[device.pci_dev]:1,[device.pci_bus]:1,[device.irq_line]:1,[device.io_addr]:8
 
; 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 .err2 ; If an error occured, exit
 
mov [device.type], NET_TYPE_ETH
call NetRegDev
 
cmp eax, -1
je .destroy
 
ret
 
; If the device was already loaded, find the device number and return it in eax
 
.find_devicenum:
DEBUGF 2,"Trying to find device number of already registered device\n"
call NetPtrToNum ; This kernel procedure converts a pointer to device struct in ebx
; into a device number in edi
mov eax, edi ; Application wants it in eax instead
DEBUGF 2,"Kernel says: %u\n", eax
ret
 
; If an error occured, remove all allocated data and exit (returning -1 in eax)
 
.destroy:
; todo: reset device into virgin state
 
.err2:
dec [devices]
.err:
DEBUGF 2,"removing device structure\n"
stdcall KernelFree, ebx
.fail:
or eax, -1
ret
 
;------------------------------------------------------
endp
 
 
align 4
unload:
 
ret
 
 
align 4
init_board:
 
DEBUGF 1,"init_board\n"
 
PCI_make_bus_master
 
; Soft reset the chip
set_io 0
set_io REG_ChipCmd
mov al, CMD_Reset
out dx, al
 
; Check that the chip has finished the reset
mov ecx, 1000
set_io REG_ChipCmd
@@: in al, dx
test al, CMD_Reset
jz @f
udelay 10
loop @b
@@:
; identify config method
set_io REG_TxConfig
in eax, dx
and eax, 0x7c800000
DEBUGF 1,"init_board: TxConfig & 0x7c800000 = 0x%x\n", eax
mov esi, mac_info-8
@@: add esi, 8
mov ecx, eax
and ecx, [esi]
cmp ecx, [esi]
jne @b
mov eax, [esi+4]
mov [tpc.mcfg], eax
 
mov [tpc.pcfg], PCFG_METHOD_3
READ_GMII_REG 3
and al, 0x0f
or al, al
jnz @f
mov [tpc.pcfg], PCFG_METHOD_1
jmp .pconf
@@: dec al
jnz .pconf
mov [tpc.pcfg], PCFG_METHOD_2
.pconf:
 
; identify chip attached to board
mov ecx, 10
mov eax, [tpc.mcfg]
@@: dec ecx
js @f
cmp eax, [rtl_chip_info + ecx*8]
jne @b
mov [tpc.chipset], ecx
jmp .match
@@:
; if unknown chip, assume array element #0, original RTL-8169 in this case
DEBUGF 1,"init_board: PCI device: unknown chip version, assuming RTL-8169\n"
set_io REG_TxConfig
in eax, dx
DEBUGF 1,"init_board: PCI device: TxConfig = 0x%x\n", eax
 
mov [tpc.chipset], 0
 
xor eax, eax
inc eax
ret
 
.match:
DEBUGF 1,"init_board: chipset=%u\n", ecx
xor eax,eax
ret
 
 
 
;***************************************************************************
; Function
; probe
; Description
; Searches for an ethernet card, enables it and clears the rx buffer
; If a card was found, it enables the ethernet -> TCPIP link
; Destroyed registers
; eax, ebx, ecx, edx
;
;***************************************************************************
align 4
probe:
 
DEBUGF 1,"probe\n"
 
call init_board
call read_mac
call PHY_config
 
; DEBUGF 1,"K : Set MAC Reg C+CR Offset 0x82h = 0x01h\n"
set_io 0
set_io 0x82
mov al, 0x01
out dx, al
cmp [tpc.mcfg], MCFG_METHOD_03
jae @f
; DEBUGF 1,"K : Set PCI Latency=0x40\n"
; stdcall pci_write_config_byte,PCI_LATENCY_TIMER,0x40
@@:
cmp [tpc.mcfg], MCFG_METHOD_02
jne @f
; DEBUGF 1,"K : Set MAC Reg C+CR Offset 0x82h = 0x01h\n"
set_io 0x82
mov al, 0x01
out dx, al
; DEBUGF 1,"K : Set PHY Reg 0x0bh = 0x00h\n"
WRITE_GMII_REG 0x0b, 0x0000 ; w 0x0b 15 0 0
@@:
; if TBI is not enabled
set_io 0
set_io REG_PHYstatus
in al, dx
test al, PHYS_TBI_Enable
jz .tbi_dis
READ_GMII_REG PHY_AUTO_NEGO_REG
 
; enable 10/100 Full/Half Mode, leave PHY_AUTO_NEGO_REG bit4:0 unchanged
and eax, 0x0C1F
or eax, PHY_Cap_10_Half or PHY_Cap_10_Full or PHY_Cap_100_Half or PHY_Cap_100_Full
WRITE_GMII_REG PHY_AUTO_NEGO_REG, ax
 
; enable 1000 Full Mode
WRITE_GMII_REG PHY_1000_CTRL_REG, PHY_Cap_1000_Full or PHY_Cap_1000_Half ; rtl8168
 
; Enable auto-negotiation and restart auto-nigotiation
WRITE_GMII_REG PHY_CTRL_REG, PHY_Enable_Auto_Nego or PHY_Restart_Auto_Nego
 
udelay 100
mov ecx, 10000
; wait for auto-negotiation process
@@: dec ecx
jz @f
set_io 0
READ_GMII_REG PHY_STAT_REG
udelay 100
test eax, PHY_Auto_Neco_Comp
jz @b
set_io REG_PHYstatus
in al, dx
jmp @f
.tbi_dis:
udelay 100
@@:
 
 
;***************************************************************************
; Function
; rt8169_reset
; Description
; Place the chip (ie, the ethernet card) into a virgin state
; Destroyed registers
; eax, ebx, ecx, edx
;
;***************************************************************************
align 4
reset:
 
DEBUGF 1,"reset\n"
 
lea eax, [device.tx_ring]
mov [tpc.TxDescArrays], eax
mov [tpc.TxDescArray], eax
 
lea eax, [device.rx_ring]
mov [tpc.RxDescArrays], eax
mov [tpc.RxDescArray], eax
 
call init_ring
call hw_start
 
; clear packet/byte counters
 
xor eax, eax
lea edi, [device.bytes_tx]
mov ecx, 6
rep stosd
 
mov [device.mtu], 1500
 
; Set link state to unknown
mov [device.state], ETH_LINK_UNKOWN
 
xor eax, eax
ret
 
 
 
 
 
align 4
PHY_config:
 
DEBUGF 1,"hw_PHY_config: priv.mcfg=%d, priv.pcfg=%d\n",[tpc.mcfg],[tpc.pcfg]
 
cmp [tpc.mcfg], MCFG_METHOD_04
jne .not_4
set_io 0
; WRITE_GMII_REG 0x1F, 0x0001
; WRITE_GMII_REG 0x1b, 0x841e
; WRITE_GMII_REG 0x0e, 0x7bfb
; WRITE_GMII_REG 0x09, 0x273a
WRITE_GMII_REG 0x1F, 0x0002
WRITE_GMII_REG 0x01, 0x90D0
WRITE_GMII_REG 0x1F, 0x0000
jmp .exit
.not_4:
cmp [tpc.mcfg], MCFG_METHOD_02
je @f
cmp [tpc.mcfg], MCFG_METHOD_03
jne .not_2_or_3
@@:
set_io 0
WRITE_GMII_REG 0x1F, 0x0001
WRITE_GMII_REG 0x15, 0x1000
WRITE_GMII_REG 0x18, 0x65C7
WRITE_GMII_REG 0x04, 0x0000
WRITE_GMII_REG 0x03, 0x00A1
WRITE_GMII_REG 0x02, 0x0008
WRITE_GMII_REG 0x01, 0x1020
WRITE_GMII_REG 0x00, 0x1000
WRITE_GMII_REG 0x04, 0x0800
WRITE_GMII_REG 0x04, 0x0000
WRITE_GMII_REG 0x04, 0x7000
WRITE_GMII_REG 0x03, 0xFF41
WRITE_GMII_REG 0x02, 0xDE60
WRITE_GMII_REG 0x01, 0x0140
WRITE_GMII_REG 0x00, 0x0077
WRITE_GMII_REG 0x04, 0x7800
WRITE_GMII_REG 0x04, 0x7000
WRITE_GMII_REG 0x04, 0xA000
WRITE_GMII_REG 0x03, 0xDF01
WRITE_GMII_REG 0x02, 0xDF20
WRITE_GMII_REG 0x01, 0xFF95
WRITE_GMII_REG 0x00, 0xFA00
WRITE_GMII_REG 0x04, 0xA800
WRITE_GMII_REG 0x04, 0xA000
WRITE_GMII_REG 0x04, 0xB000
WRITE_GMII_REG 0x03, 0xFF41
WRITE_GMII_REG 0x02, 0xDE20
WRITE_GMII_REG 0x01, 0x0140
WRITE_GMII_REG 0x00, 0x00BB
WRITE_GMII_REG 0x04, 0xB800
WRITE_GMII_REG 0x04, 0xB000
WRITE_GMII_REG 0x04, 0xF000
WRITE_GMII_REG 0x03, 0xDF01
WRITE_GMII_REG 0x02, 0xDF20
WRITE_GMII_REG 0x01, 0xFF95
WRITE_GMII_REG 0x00, 0xBF00
WRITE_GMII_REG 0x04, 0xF800
WRITE_GMII_REG 0x04, 0xF000
WRITE_GMII_REG 0x04, 0x0000
WRITE_GMII_REG 0x1F, 0x0000
WRITE_GMII_REG 0x0B, 0x0000
jmp .exit
.not_2_or_3:
DEBUGF 1,"tpc.mcfg=%d, discard hw PHY config\n", [tpc.mcfg]
.exit:
ret
 
 
 
align 4
set_rx_mode:
 
DEBUGF 1,"set_rx_mode\n"
 
; IFF_ALLMULTI
; Too many to filter perfectly -- accept all multicasts
set_io 0
set_io REG_RxConfig
in eax, dx
mov ecx, [tpc.chipset]
and eax, [rtl_chip_info + ecx * 8 + 4] ; RxConfigMask
or eax, rx_config or (RXM_AcceptBroadcast or RXM_AcceptMulticast or RXM_AcceptMyPhys)
out dx, eax
 
; Multicast hash filter
set_io REG_MAR0 + 0
or eax, -1
out dx, eax
set_io REG_MAR0 + 4
out dx, eax
 
ret
 
 
align 4
init_ring:
 
DEBUGF 1,"init_ring\n"
 
xor eax, eax
mov [tpc.cur_rx], eax
mov [tpc.cur_tx], eax
 
lea edi, [device.tx_ring]
mov ecx, (NUM_TX_DESC * tx_desc.size) / 4
rep stosd
 
lea edi, [device.rx_ring]
mov ecx, (NUM_RX_DESC * rx_desc.size) / 4
rep stosd
 
mov edi, [tpc.RxDescArray]
mov ecx, NUM_RX_DESC
.loop:
push ecx
stdcall KernelAlloc, RX_BUF_SIZE
mov [edi + rx_desc.buf_soft_addr], eax
call GetPgAddr
mov dword [edi + rx_desc.buf_addr], eax
mov [edi + rx_desc.status], DSB_OWNbit or RX_BUF_SIZE
add edi, rx_desc.size
pop ecx
loop .loop
or [edi - rx_desc.size + rx_desc.status], DSB_EORbit
 
ret
 
 
align 4
hw_start:
 
DEBUGF 1,"hw_start\n"
 
; attach int handler
movzx eax, [device.irq_line]
DEBUGF 1,"Attaching int handler to irq %x\n", eax:1
stdcall AttachIntHandler, eax, int_handler, dword 0
 
; Soft reset the chip
set_io 0
set_io REG_ChipCmd
mov al, CMD_Reset
out dx, al
 
DEBUGF 1,"Waiting for chip to reset... "
; Check that the chip has finished the reset
mov ecx, 1000
set_io REG_ChipCmd
@@: in al, dx
test al, CMD_Reset
jz @f
udelay 10
loop @b
@@:
DEBUGF 1,"done!\n"
 
set_io REG_Cfg9346
mov al, CFG_9346_Unlock
out dx, al
 
set_io REG_ChipCmd
mov al, CMD_TxEnb or CMD_RxEnb
out dx, al
 
set_io REG_ETThReg
mov al, ETTh
out dx, al
 
; For gigabit rtl8169
set_io REG_RxMaxSize
mov ax, RxPacketMaxSize
out dx, ax
 
; Set Rx Config register
set_io REG_RxConfig
in ax, dx
mov ecx, [tpc.chipset]
and eax, [rtl_chip_info + ecx * 8 + 4] ; RxConfigMask
or eax, rx_config
out dx, eax
 
; Set DMA burst size and Interframe Gap Time
set_io REG_TxConfig
mov eax, (TX_DMA_BURST shl TXC_DMAShift) or (InterFrameGap shl TXC_InterFrameGapShift)
out dx, eax
 
set_io REG_CPlusCmd
in ax, dx
out dx, ax
 
in ax, dx
or ax, 1 shl 3
cmp [tpc.mcfg], MCFG_METHOD_02
jne @f
cmp [tpc.mcfg], MCFG_METHOD_03
jne @f
or ax,1 shl 14
DEBUGF 1,"Set MAC Reg C+CR Offset 0xE0: bit-3 and bit-14\n"
jmp .set
@@:
DEBUGF 1,"Set MAC Reg C+CR Offset 0xE0: bit-3\n"
.set:
set_io REG_CPlusCmd
out dx, ax
 
set_io 0xE2
; mov ax, 0x1517
; out dx, ax
; mov ax, 0x152a
; out dx, ax
; mov ax, 0x282a
; out dx, ax
xor ax, ax
out dx, ax
 
xor eax, eax
mov [tpc.cur_rx], eax
lea eax, [device.tx_ring]
GetRealAddr
set_io REG_TxDescStartAddr
out dx, eax
 
lea eax, [device.rx_ring]
GetRealAddr
set_io REG_RxDescStartAddr
out dx, eax
 
set_io REG_Cfg9346
mov al, CFG_9346_Lock
out dx, al
 
udelay 10
 
xor eax, eax
set_io REG_RxMissed
out dx, eax
 
call set_rx_mode
 
set_io 0
; no early-rx interrupts
set_io REG_MultiIntr
in ax, dx
and ax, 0xF000
out dx, ax
 
; set interrupt mask
set_io REG_IntrMask
mov ax, intr_mask
out dx, ax
 
xor eax, eax
ret
 
 
align 4
read_mac:
 
set_io 0
set_io REG_MAC0
xor ecx, ecx
lea edi, [device.mac]
mov ecx, 6
 
; Get MAC address. FIXME: read EEPROM
@@: in al, dx
stosb
inc edx
loop @r
 
DEBUGF 1,"MAC = %x-%x-%x-%x-%x-%x\n",\
[device.mac+0]:2,[device.mac+1]:2,[device.mac+2]:2,[device.mac+3]:2,[device.mac+4]:2,[device.mac+5]:2
 
ret
 
align 4
write_mac:
 
ret 6
 
 
 
 
 
;***************************************************************************
; Function
; transmit
; Description
; Transmits a packet of data via the ethernet card
;
; Destroyed registers
; eax, edx, esi, edi
;
;***************************************************************************
align 4
transmit:
 
DEBUGF 1,"Transmitting packet, buffer:%x, size:%u\n", [esp+4], [esp+8]
mov eax, [esp+4]
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 dword [esp+8], MAX_ETH_FRAME_SIZE
ja .fail
 
;----------------------------------
; Find currentTX descriptor address
 
mov eax, tx_desc.size
mul [tpc.cur_tx]
lea esi, [eax + device.tx_ring]
 
DEBUGF 1,"Using TX desc: %x\n", esi
 
;---------------------------
; Program the packet pointer
 
mov eax, [esp + 4]
mov [esi + tx_desc.buf_soft_addr], eax
GetRealAddr
mov dword [esi + tx_desc.buf_addr], eax
 
;------------------------
; Program the packet size
 
mov eax, [esp + 8]
@@: or eax, DSB_OWNbit or DSB_FSbit or DSB_LSbit
cmp [tpc.cur_tx], NUM_TX_DESC - 1
jne @f
or eax, DSB_EORbit
@@: mov [esi + tx_desc.status], eax
 
;-----------------------------------------
; Set the polling bit (start transmission)
 
set_io 0
set_io REG_TxPoll
mov al, 0x40 ; set polling bit
out dx, al
 
;-----------------------
; Update TX descriptor
 
inc [tpc.cur_tx]
and [tpc.cur_tx], NUM_TX_DESC - 1
 
;-------------
; Update stats
 
inc [device.packets_tx]
mov eax, [esp + 8]
add dword [device.bytes_tx], eax
adc dword [device.bytes_tx + 4], 0
 
xor eax, eax
ret 8
 
.fail:
DEBUGF 1,"transmit failed\n"
or eax, -1
stdcall KernelFree, [esp+4]
ret 8
 
 
;;;DSB_OWNbit
 
 
;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Interrupt handler ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
int_handler:
 
push ebx esi edi
 
DEBUGF 1,"\n%s int\n", my_service
 
; find pointer of device wich made IRQ occur
 
mov ecx, [devices]
test ecx, ecx
jz .nothing
mov esi, device_list
.nextdevice:
mov ebx, [esi]
 
set_io 0
set_io REG_IntrStatus
in ax, dx
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,"Device: %x Status: %x ", ebx, ax
 
cmp ax, 0xFFFF ; if so, hardware is no longer present
je .fail
 
;--------
; Receive
 
test ax, ISB_RxOK
jz .no_rx
 
push ax
push ebx
 
.check_more:
pop ebx
DEBUGF 1,"ebx = 0x%x\n", ebx
mov eax, rx_desc.size
mul [tpc.cur_rx]
lea esi, [eax + device.rx_ring]
 
DEBUGF 1,"RxDesc.status = 0x%x\n", [esi + rx_desc.status]
 
mov eax, [esi + rx_desc.status]
test eax, DSB_OWNbit ;;;
jnz .rx_return
 
DEBUGF 1,"tpc.cur_rx = %u\n", [tpc.cur_rx]
 
test eax, SD_RxRES
jnz .rx_return ;;;;; RX error!
 
push ebx
push .check_more
and eax, 0x00001FFF
add eax, -4 ; we dont need CRC
push eax
DEBUGF 1,"data length = %u\n", ax
 
;-------------
; Update stats
 
add dword [device.bytes_rx], eax
adc dword [device.bytes_rx + 4], 0
inc dword [device.packets_rx]
 
push [esi + rx_desc.buf_soft_addr]
 
;----------------------
; Allocate a new buffer
 
stdcall KernelAlloc, RX_BUF_SIZE
mov [esi + rx_desc.buf_soft_addr], eax
GetRealAddr
mov dword [esi + rx_desc.buf_addr], eax
 
;---------------
; re set OWN bit
 
mov eax, DSB_OWNbit or RX_BUF_SIZE
cmp [tpc.cur_rx], NUM_RX_DESC - 1
jne @f
or eax, DSB_EORbit
@@: mov [esi + rx_desc.status], eax
 
;--------------
; Update rx ptr
 
inc [tpc.cur_rx]
and [tpc.cur_rx], NUM_RX_DESC - 1
 
jmp Eth_input
.rx_return:
 
pop ax
.no_rx:
 
;---------
; Transmit
 
test ax, ISB_TxOK
jz .no_tx
push ax
 
DEBUGF 1,"TX ok!\n"
 
mov ecx, NUM_TX_DESC
lea esi, [device.tx_ring]
.txloop:
cmp [esi + tx_desc.buf_soft_addr], 0
jz .maybenext
 
test [esi + tx_desc.status], DSB_OWNbit
jnz .maybenext
 
push ecx
DEBUGF 1,"Freeing up TX desc: %x\n", esi
stdcall KernelFree, [esi + tx_desc.buf_soft_addr]
pop ecx
and [esi + tx_desc.buf_soft_addr], 0
 
.maybenext:
add esi, tx_desc.size
dec ecx
jnz .txloop
 
pop ax
.no_tx:
 
;-------
; Finish
 
set_io 0
set_io REG_IntrStatus
out dx, ax ; ACK all interrupts
 
.fail:
pop edi esi ebx
xor eax, eax
inc eax
 
ret
 
 
 
 
 
 
 
 
 
; End of code
align 4 ; Place all initialised data here
 
devices dd 0
version dd (DRIVER_VERSION shl 16) or (API_VERSION and 0xFFFF)
my_service db 'RTL8169',0 ; max 16 chars include zero
 
include_debug_strings ; All data wich FDO uses will be included here
 
rtl_chip_info dd \
MCFG_METHOD_01, 0xff7e1880, \ ; RTL8169
MCFG_METHOD_02, 0xff7e1880, \ ; RTL8169s/8110s
MCFG_METHOD_03, 0xff7e1880, \ ; RTL8169s/8110s
MCFG_METHOD_04, 0xff7e1880, \ ; RTL8169sb/8110sb
MCFG_METHOD_05, 0xff7e1880, \ ; RTL8169sc/8110sc
MCFG_METHOD_11, 0xff7e1880, \ ; RTL8168b/8111b // PCI-E
MCFG_METHOD_12, 0xff7e1880, \ ; RTL8168b/8111b // PCI-E
MCFG_METHOD_13, 0xff7e1880, \ ; RTL8101e // PCI-E 8139
MCFG_METHOD_14, 0xff7e1880, \ ; RTL8100e // PCI-E 8139
MCFG_METHOD_15, 0xff7e1880 ; RTL8100e // PCI-E 8139
 
mac_info dd \
0x38800000, MCFG_METHOD_15, \
0x38000000, MCFG_METHOD_12, \
0x34000000, MCFG_METHOD_13, \
0x30800000, MCFG_METHOD_14, \
0x30000000, MCFG_METHOD_11, \
0x18000000, MCFG_METHOD_05, \
0x10000000, MCFG_METHOD_04, \
0x04000000, MCFG_METHOD_03, \
0x00800000, MCFG_METHOD_02, \
0x00000000, MCFG_METHOD_01 ; catch-all
 
name_01 db "RTL8169", 0
name_02_03 db "RTL8169s/8110s", 0
name_04 db "RTL8169sb/8110sb", 0
name_05 db "RTL8169sc/8110sc", 0
name_11_12 db "RTL8168b/8111b", 0 ; PCI-E
name_13 db "RTL8101e", 0 ; PCI-E 8139
name_14_15 db "RTL8100e", 0 ; PCI-E 8139
 
 
section '.data' data readable writable align 16 ; place all uninitialized data place here
 
device_list rd MAX_DEVICES ; This list contains all pointers to device structures the driver is handling
 
 
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
/drivers/ethernet/bcm57xx.asm
0,0 → 1,424
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; Broadcom NetXtreme 57xx driver for KolibriOS ;;
;; ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;; Broadcom's programmers's manual for the BCM57xx ;;
;; http://www.broadcom.com/collateral/pg/57XX-PG105-R.pdf ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
; TODO: make better use of the available descriptors
 
format MS COFF
 
API_VERSION = 0x01000100
DRIVER_VERSION = 5
 
MAX_DEVICES = 16
 
DEBUG = 1
__DEBUG__ = 1
__DEBUG_LEVEL__ = 2
 
 
include '../proc32.inc'
include '../imports.inc'
include '../fdo.inc'
include '../netdrv.inc'
 
public START
public service_proc
public version
 
 
virtual at ebx
device:
ETH_DEVICE
 
.mmio_addr dd ?
.pci_bus dd ?
.pci_dev dd ?
.irq_line db ?
 
.cur_tx dd ?
.last_tx dd ?
 
rb 0x100 - (($ - device) and 0xff)
.rx_desc rd 256/8
 
rb 0x100 - (($ - device) and 0xff)
.tx_desc rd 256/8
 
sizeof.device_struct = $ - device
 
end virtual
 
section '.flat' code readable align 16
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; proc START ;;
;; ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
proc START stdcall, state:dword
 
cmp [state], 1
jne .exit
 
.entry:
 
DEBUGF 2,"Loading %s driver\n", my_service
stdcall RegService, my_service, service_proc
ret
 
.fail:
.exit:
xor eax, eax
ret
 
endp
 
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; proc SERVICE_PROC ;;
;; ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
proc service_proc stdcall, ioctl:dword
 
mov edx, [ioctl]
mov eax, [IOCTL.io_code]
 
;------------------------------------------------------
 
cmp eax, 0 ;SRV_GETVERSION
jne @F
 
cmp [IOCTL.out_size], 4
jb .fail
mov eax, [IOCTL.output]
mov [eax], dword API_VERSION
 
xor eax, eax
ret
 
;------------------------------------------------------
@@:
cmp eax, 1 ;SRV_HOOK
jne .fail
 
cmp [IOCTL.inp_size], 3 ; Data input must be at least 3 bytes
jb .fail
 
mov eax, [IOCTL.input]
cmp byte [eax], 1 ; 1 means device number and bus number (pci) are given
jne .fail ; other types arent supported for this card yet
 
; check if the device is already listed
 
mov esi, device_list
mov ecx, [devices]
test ecx, ecx
jz .firstdevice
 
; mov eax, [IOCTL.input] ; get the pci bus and device numbers
mov ax, [eax+1] ;
.nextdevice:
mov ebx, [esi]
cmp al, byte [device.pci_bus]
jne .next
cmp ah, byte [device.pci_dev]
je .find_devicenum ; Device is already loaded, let's find it's device number
.next:
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_struct, .fail ; Allocate the buffer for device structure
 
; Fill in the direct call addresses into the struct
 
mov [device.reset], reset
mov [device.transmit], transmit
mov [device.unload], unload
mov [device.name], my_service
 
; save the pci bus and device numbers
 
mov eax, [IOCTL.input]
movzx ecx, byte [eax+1]
mov [device.pci_bus], ecx
movzx ecx, byte [eax+2]
mov [device.pci_dev], ecx
 
; Now, it's time to find the base mmio addres of the PCI device
 
PCI_find_mmio32
 
; Create virtual mapping of the physical memory
 
push 1Bh ; PG_SW+PG_NOCACHE
push 10000h ; size of the map
push eax
call MapIoMem
mov [device.mmio_addr], eax
 
; We've found the mmio address, find IRQ now
 
PCI_find_irq
 
DEBUGF 1,"Hooking into device, dev:%x, bus:%x, irq:%x, addr:%x\n",\
[device.pci_dev]:1,[device.pci_bus]:1,[device.irq_line]:1,[device.mmio_addr]:8
 
; Ok, the eth_device structure is ready, let's probe the device
call probe ; this function will output in eax
test eax, eax
jnz .err ; If an error occured, exit
 
mov eax, [devices] ; Add the device structure to our device list
mov [device_list+4*eax], ebx ; (IRQ handler uses this list to find device)
inc [devices] ;
 
mov [device.type], NET_TYPE_ETH
call 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"
call NetPtrToNum ; This kernel procedure converts a pointer to device struct in ebx
; into a device number in edi
mov eax, edi ; Application wants it in eax instead
DEBUGF 1,"Kernel says: %u\n", eax
ret
 
; If an error occured, remove all allocated data and exit (returning -1 in eax)
 
.destroy:
; todo: reset device into virgin state
 
.err:
stdcall KernelFree, ebx
 
.fail:
or eax, -1
ret
 
;------------------------------------------------------
endp
 
 
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
;; ;;
;; Actual Hardware dependent code starts here ;;
;; ;;
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
 
 
align 4
unload:
; TODO: (in this particular order)
;
; - Stop the device
; - Detach int handler
; - Remove device from local list (device_list)
; - call unregister function in kernel
; - Remove all allocated structures and buffers the card used
 
or eax, -1
 
ret
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; probe: enables the device (if it really is I8254X)
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
align 4
probe:
 
DEBUGF 1,"Probe\n"
 
PCI_make_bus_master
 
; TODO: validate the device
 
 
 
 
 
align 4
reset:
 
DEBUGF 1,"Reset\n"
 
movzx eax, [device.irq_line]
DEBUGF 1,"Attaching int handler to irq %x\n", eax:1
stdcall AttachIntHandler, eax, int_handler, dword 0
test eax, eax
jnz @f
DEBUGF 1,"\nCould not attach int handler!\n"
; or eax, -1
; ret
@@:
 
call read_mac
 
; Set the mtu, kernel will be able to send now
mov [device.mtu], 1514
 
; Set link state to unknown
mov [device.state], ETH_LINK_UNKOWN
 
ret
 
 
 
 
align 4
read_mac:
 
DEBUGF 1,"Read MAC\n"
 
mov esi, [device.mmio_addr]
lea edi, [device.mac]
movsd
movsw
 
.mac_ok:
DEBUGF 1,"MAC = %x-%x-%x-%x-%x-%x\n",\
[device.mac+0]:2,[device.mac+1]:2,[device.mac+2]:2,[device.mac+3]:2,[device.mac+4]:2,[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
transmit:
DEBUGF 2,"\nTransmitting packet, buffer:%x, size:%u\n", [esp+4], [esp+8]
mov eax, [esp+4]
DEBUGF 2,"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 dword [esp + 8], 1514
ja .fail
cmp dword [esp + 8], 60
jb .fail
 
 
 
 
; Update stats
inc [device.packets_tx]
mov eax, [esp + 8]
add dword [device.bytes_tx], eax
adc dword [device.bytes_tx + 4], 0
 
ret 8
 
.fail:
DEBUGF 1,"Send failed\n"
ret 8
 
 
;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Interrupt handler ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
int_handler:
 
push ebx esi edi
 
DEBUGF 1,"\n%s int\n", my_service
;-------------------------------------------
; 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 edi, [device.mmio_addr]
; mov eax, [edi + REG_ICR]
test eax, eax
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 ", ebx, eax
 
pop edi esi ebx
xor eax, eax
inc eax
 
ret
 
 
 
 
; End of code
 
section '.data' data readable writable align 16
align 4
 
devices dd 0
version dd (DRIVER_VERSION shl 16) or (API_VERSION and 0xFFFF)
my_service db 'BCM57XX',0 ; max 16 chars include zero
 
include_debug_strings ; All data wich FDO uses will be included here
 
device_list rd MAX_DEVICES ; This list contains all pointers to device structures the driver is handling
 
 
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
/drivers/ethernet/dec21x4x.asm
0,0 → 1,1702
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; DEC 21x4x driver for KolibriOS ;;
;; ;;
;; Based on dec21140.Asm from Solar OS by ;;
;; Eugen Brasoveanu, ;;
;; Ontanu Bogdan Valentin ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
format MS COFF
 
API_VERSION = 0x01000100
DRIVER_VERSION = 5
 
MAX_DEVICES = 16
 
RX_DES_COUNT = 4 ; no of RX descriptors, must be power of 2
RX_BUFF_SIZE = 2048 ; size of buffer for each descriptor, must be multiple of 4 and <= 2048 TDES1_TBS1_MASK
 
TX_DES_COUNT = 4 ; no of TX descriptors, must be power of 2
TX_BUFF_SIZE = 2048 ; size of buffer for each descriptor, used for memory allocation only
 
 
DEBUG = 1
__DEBUG__ = 1
__DEBUG_LEVEL__ = 2
 
include '../proc32.inc'
include '../imports.inc'
include '../fdo.inc'
include '../netdrv.inc'
 
public START
public service_proc
public version
 
virtual at ebx
 
device:
 
ETH_DEVICE
 
.rx_p_des dd ? ; descriptors ring with received packets
.tx_p_des dd ? ; descriptors ring with 'to transmit' packets
.tx_free_des dd ? ; Tx descriptors available
.tx_wr_des dd ? ; Tx current descriptor to write data to
.tx_rd_des dd ? ; Tx current descriptor to read TX completion
.rx_crt_des dd ? ; Rx current descriptor
 
.io_addr dd ?
.pci_bus dd ?
.pci_dev dd ?
.irq_line db ?
 
.size = $ - device
 
end virtual
 
;-------------------------------------------
; configuration registers
;-------------------------------------------
CFCS = 4 ; configuration and status register
 
CSR0 = 0x00 ; Bus mode
CSR1 = 0x08 ; Transmit Poll Command
CSR2 = 0x10 ; Receive Poll Command
CSR3 = 0x18 ; Receive list base address
CSR4 = 0x20 ; Transmit list base address
CSR5 = 0x28 ; Status
CSR6 = 0x30 ; Operation mode
CSR7 = 0x38 ; Interrupt enable
CSR8 = 0x40 ; Missed frames and overflow counter
CSR9 = 0x48 ; Boot ROM, serial ROM, and MII management
CSR10 = 0x50 ; Boot ROM programming address
CSR11 = 0x58 ; General-purpose timer
CSR12 = 0x60 ; General-purpose port
CSR13 = 0x68
CSR14 = 0x70
CSR15 = 0x78 ; Watchdog timer
 
;--------bits/commands of CSR0-------------------
CSR0_RESET = 1b
 
CSR0_WIE = 1 shl 24 ; Write and Invalidate Enable
CSR0_RLE = 1 shl 23 ; PCI Read Line Enable
CSR0_RML = 1 shl 21 ; PCI Read Multiple
 
CSR0_CACHEALIGN_NONE = 00b shl 14
CSR0_CACHEALIGN_32 = 01b shl 14
CSR0_CACHEALIGN_64 = 10b shl 14
CSR0_CACHEALIGN_128 = 11b shl 14
 
; using values from linux driver..
CSR0_DEFAULT = CSR0_WIE + CSR0_RLE + CSR0_RML + CSR0_CACHEALIGN_NONE
 
;------- CSR5 -STATUS- bits --------------------------------
CSR5_TI = 1 shl 0 ; Transmit interupt - frame transmition completed
CSR5_TPS = 1 shl 1 ; Transmit process stopped
CSR5_TU = 1 shl 2 ; Transmit Buffer unavailable
CSR5_TJT = 1 shl 3 ; Transmit Jabber Timeout (transmitter had been excessively active)
CSR5_UNF = 1 shl 5 ; Transmit underflow - FIFO underflow
CSR5_RI = 1 shl 6 ; Receive Interrupt
CSR5_RU = 1 shl 7 ; Receive Buffer unavailable
CSR5_RPS = 1 shl 8 ; Receive Process stopped
CSR5_RWT = 1 shl 9 ; Receive Watchdow Timeout
CSR5_ETI = 1 shl 10 ; Early transmit Interrupt
CSR5_GTE = 1 shl 11 ; General Purpose Timer Expired
CSR5_FBE = 1 shl 13 ; Fatal bus error
CSR5_ERI = 1 shl 14 ; Early receive Interrupt
CSR5_AIS = 1 shl 15 ; Abnormal interrupt summary
CSR5_NIS = 1 shl 16 ; normal interrupt summary
CSR5_RS_SH = 17 ; Receive process state -shift
CSR5_RS_MASK = 111b ; -mask
CSR5_TS_SH = 20 ; Transmit process state -shift
CSR5_TS_MASK = 111b ; -mask
CSR5_EB_SH = 23 ; Error bits -shift
CSR5_EB_MASK = 111b ; Error bits -mask
 
;CSR5 TS values
CSR5_TS_STOPPED = 000b
CSR5_TS_RUNNING_FETCHING_DESC = 001b
CSR5_TS_RUNNING_WAITING_TX = 010b
CSR5_TS_RUNNING_READING_BUFF = 011b
CSR5_TS_RUNNING_SETUP_PCKT = 101b
CSR5_TS_SUSPENDED = 110b
CSR5_TS_RUNNING_CLOSING_DESC = 111b
 
;------- CSR6 -OPERATION MODE- bits --------------------------------
CSR6_HP = 1 shl 0 ; Hash/Perfect Receive Filtering mode
CSR6_SR = 1 shl 1 ; Start/Stop receive
CSR6_HO = 1 shl 2 ; Hash only Filtering mode
CSR6_PB = 1 shl 3 ; Pass bad frames
CSR6_IF = 1 shl 4 ; Inverse filtering
CSR6_SB = 1 shl 5 ; Start/Stop backoff counter
CSR6_PR = 1 shl 6 ; Promiscuos mode -default after reset
CSR6_PM = 1 shl 7 ; Pass all multicast
CSR6_F = 1 shl 9 ; Full Duplex mode
CSR6_OM_SH = 10 ; Operating Mode -shift
CSR6_OM_MASK = 11b ; -mask
CSR6_FC = 1 shl 12 ; Force Collision Mode
CSR6_ST = 1 shl 13 ; Start/Stop Transmission Command
CSR6_TR_SH = 14 ; Threshold Control -shift
CSR6_TR_MASK = 11b ; -mask
CSR6_CA = 1 shl 17 ; Capture Effect Enable
CSR6_PS = 1 shl 18 ; Port select SRL / MII/SYM
CSR6_HBD = 1 shl 19 ; Heartbeat Disable
CSR6_SF = 1 shl 21 ; Store and Forward -transmit full packet only
CSR6_TTM = 1 shl 22 ; Transmit Threshold Mode -
CSR6_PCS = 1 shl 23 ; PCS active and MII/SYM port operates in symbol mode
CSR6_SCR = 1 shl 24 ; Scrambler Mode
CSR6_MBO = 1 shl 25 ; Must Be One
CSR6_RA = 1 shl 30 ; Receive All
CSR6_SC = 1 shl 31 ; Special Capture Effect Enable
 
 
;------- CSR7 -INTERRUPT ENABLE- bits --------------------------------
CSR7_TI = 1 shl 0 ; transmit Interrupt Enable (set with CSR7<16> & CSR5<0> )
CSR7_TS = 1 shl 1 ; transmit Stopped Enable (set with CSR7<15> & CSR5<1> )
CSR7_TU = 1 shl 2 ; transmit buffer underrun Enable (set with CSR7<16> & CSR5<2> )
CSR7_TJ = 1 shl 3 ; transmit jabber timeout enable (set with CSR7<15> & CSR5<3> )
CSR7_UN = 1 shl 5 ; underflow Interrupt enable (set with CSR7<15> & CSR5<5> )
CSR7_RI = 1 shl 6 ; receive Interrupt enable (set with CSR7<16> & CSR5<5> )
CSR7_RU = 1 shl 7 ; receive buffer unavailable enable (set with CSR7<15> & CSR5<7> )
CSR7_RS = 1 shl 8 ; Receive stopped enable (set with CSR7<15> & CSR5<8> )
CSR7_RW = 1 shl 9 ; receive watchdog timeout enable (set with CSR7<15> & CSR5<9> )
CSR7_ETE = 1 shl 10 ; Early transmit Interrupt enable (set with CSR7<15> & CSR5<10> )
CSR7_GPT = 1 shl 11 ; general purpose timer enable (set with CSR7<15> & CSR5<11> )
CSR7_FBE = 1 shl 13 ; Fatal bus error enable (set with CSR7<15> & CSR5<13> )
CSR7_ERE = 1 shl 14 ; Early receive enable (set with CSR7<16> & CSR5<14> )
CSR7_AI = 1 shl 15 ; Abnormal Interrupt Summary Enable (enables CSR5<0,3,7,8,9,10,13>)
CSR7_NI = 1 shl 16 ; Normal Interrup Enable (enables CSR5<0,2,6,11,14>)
 
CSR7_DEFAULT = CSR7_TI + CSR7_TS + CSR7_RI + CSR7_RS + CSR7_TU + CSR7_TJ + CSR7_UN + \
CSR7_RU + CSR7_RW + CSR7_FBE + CSR7_AI + CSR7_NI
 
;----------- descriptor structure ---------------------
struc DES {
.status dd ? ; bit 31 is 'own' and rest is 'status'
.length dd ? ; control bits + bytes-count buffer 1 + bytes-count buffer 2
.buffer1 dd ? ; pointer to buffer1
.buffer2 dd ? ; pointer to buffer2 or in this case to next descriptor, as we use a chained structure
.virtaddr dd ?
.size = 64 ; 64, for alignment purposes
}
 
virtual at 0
DES DES
end virtual
 
;common to Rx and Tx
DES0_OWN = 1 shl 31 ; if set, the NIC controls the descriptor, otherwise driver 'owns' the descriptors
 
;receive
RDES0_ZER = 1 shl 0 ; must be 0 if legal length :D
RDES0_CE = 1 shl 1 ; CRC error, valid only on last desc (RDES0<8>=1)
RDES0_DB = 1 shl 2 ; dribbling bit - not multiple of 8 bits, valid only on last desc (RDES0<8>=1)
RDES0_RE = 1 shl 3 ; Report on MII error.. i dont realy know what this means :P
RDES0_RW = 1 shl 4 ; received watchdog timer expiration - must set CSR5<9>, valid only on last desc (RDES0<8>=1)
RDES0_FT = 1 shl 5 ; frame type: 0->IEEE802.0 (len<1500) 1-> ETHERNET frame (len>1500), valid only on last desc (RDES0<8>=1)
RDES0_CS = 1 shl 6 ; Collision seen, valid only on last desc (RDES0<8>=1)
RDES0_TL = 1 shl 7 ; Too long(>1518)-NOT AN ERROR, valid only on last desc (RDES0<8>=1)
RDES0_LS = 1 shl 8 ; Last descriptor of current frame
RDES0_FS = 1 shl 9 ; First descriptor of current frame
RDES0_MF = 1 shl 10 ; Multicast frame, valid only on last desc (RDES0<8>=1)
RDES0_RF = 1 shl 11 ; Runt frame, valid only on last desc (RDES0<8>=1) and id overflow
RDES0_DT_SERIAL = 00b shl 12 ; Data type-Serial recv frame, valid only on last desc (RDES0<8>=1)
RDES0_DT_INTERNAL = 01b shl 12 ; Data type-Internal loopback recv frame, valid only on last desc (RDES0<8>=1)
RDES0_DT_EXTERNAL = 11b shl 12 ; Data type-External loopback recv frame, valid only on last desc (RDES0<8>=1)
RDES0_DE = 1 shl 14 ; Descriptor error - cant own a new desc and frame doesnt fit, valid only on last desc (RDES0<8>=1)
RDES0_ES = 1 shl 15 ; Error Summmary - bits 1+6+11+14, valid only on last desc (RDES0<8>=1)
RDES0_FL_SH = 16 ; Field length shift, valid only on last desc (RDES0<8>=1)
RDES0_FL_MASK = 11111111111111b ; Field length mask (+CRC), valid only on last desc (RDES0<8>=1)
RDES0_FF = 1 shl 30 ; Filtering fail-frame failed address recognition test(must CSR6<30>=1), valid only on last desc (RDES0<8>=1)
 
RDES1_RBS1_MASK = 11111111111b ; first buffer size MASK
RDES1_RBS2_SH = 11 ; second buffer size SHIFT
RDES1_RBS2_MASK = 11111111111b ; second buffer size MASK
RDES1_RCH = 1 shl 24 ; Second address chained - second address (buffer) is next desc address
RDES1_RER = 1 shl 25 ; Receive End of Ring - final descriptor, NIC must return to first desc
 
;transmition
TDES0_DE = 1 shl 0 ; Deffered
TDES0_UF = 1 shl 1 ; Underflow error
TDES0_LF = 1 shl 2 ; Link fail report (only if CSR6<23>=1)
TDES0_CC_SH = 3 ; Collision Count shift - no of collision before transmition
TDES0_CC_MASK = 1111b ; Collision Count mask
TDES0_HF = 1 shl 7 ; Heartbeat fail
TDES0_EC = 1 shl 8 ; Excessive Collisions - >16 collisions
TDES0_LC = 1 shl 9 ; Late collision
TDES0_NC = 1 shl 10 ; No carrier
TDES0_LO = 1 shl 11 ; Loss of carrier
TDES0_TO = 1 shl 14 ; Transmit Jabber Timeout
TDES0_ES = 1 shl 15 ; Error summary TDES0<1+8+9+10+11+14>=1
 
TDES1_TBS1_MASK = 11111111111b ; Buffer 1 size mask
TDES1_TBS2_SH = 11 ; Buffer 2 size shift
TDES1_TBS2_MASK = 11111111111b ; Buffer 2 size mask
TDES1_FT0 = 1 shl 22 ; Filtering type 0
TDES1_DPD = 1 shl 23 ; Disabled padding for packets <64bytes, no padding
TDES1_TCH = 1 shl 24 ; Second address chained - second buffer pointer is to next desc
TDES1_TER = 1 shl 25 ; Transmit end of ring - final descriptor
TDES1_AC = 1 shl 26 ; Add CRC disable -pretty obvious
TDES1_SET = 1 shl 27 ; Setup packet
TDES1_FT1 = 1 shl 28 ; Filtering type 1
TDES1_FS = 1 shl 29 ; First segment - buffer is first segment of frame
TDES1_LS = 1 shl 30 ; Last segment
TDES1_IC = 1 shl 31 ; Interupt on completion (CSR5<0>=1) valid when TDES1<30>=1
 
MAX_ETH_FRAME_SIZE = 1514
 
RX_MEM_TOTAL_SIZE = RX_DES_COUNT*(DES.size+RX_BUFF_SIZE)
TX_MEM_TOTAL_SIZE = TX_DES_COUNT*(DES.size+TX_BUFF_SIZE)
 
;=============================================================================
; serial ROM operations
;=============================================================================
CSR9_SR = 1 shl 11 ; SROM Select
CSR9_RD = 1 shl 14 ; ROM Read Operation
CSR9_SROM_DO = 1 shl 3 ; Data Out for SROM
CSR9_SROM_DI = 1 shl 2 ; Data In to SROM
CSR9_SROM_CK = 1 shl 1 ; clock for SROM
CSR9_SROM_CS = 1 shl 0 ; chip select.. always needed
 
; assume dx is CSR9
macro SROM_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
}
 
; assume dx is CSR9
macro MDIO_Delay {
push eax
in eax, dx
pop eax
}
 
macro Bit_Set a_bit {
in eax, dx
or eax, a_bit
out dx , eax
}
 
macro Bit_Clear a_bit {
in eax, dx
and eax, not (a_bit)
out dx, eax
}
 
 
section '.flat' code readable align 16
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; proc START ;;
;; ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
proc START stdcall, state:dword
 
cmp [state], 1
jne .exit
 
.entry:
 
DEBUGF 2,"Loading %s driver\n", my_service
stdcall RegService, my_service, service_proc
ret
 
.fail:
.exit:
xor eax, eax
ret
 
endp
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; proc SERVICE_PROC ;;
;; ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
proc service_proc stdcall, ioctl:dword
 
mov edx, [ioctl]
mov eax, [IOCTL.io_code]
 
;------------------------------------------------------
 
cmp eax, 0 ;SRV_GETVERSION
jne @F
 
cmp [IOCTL.out_size], 4
jb .fail
mov eax, [IOCTL.output]
mov [eax], dword API_VERSION
 
xor eax, eax
ret
 
;------------------------------------------------------
@@:
cmp eax, 1 ;SRV_HOOK
jne .fail
 
cmp [IOCTL.inp_size], 3 ; Data input must be at least 3 bytes
jb .fail
 
mov eax, [IOCTL.input]
cmp byte [eax], 1 ; 1 means device number and bus number (pci) are given
jne .fail ; other types arent supported for this card yet
 
; check if the device is already listed
 
mov esi, device_list
mov ecx, [devices]
test ecx, ecx
jz .firstdevice
 
; mov eax, [IOCTL.input] ; get the pci bus and device numbers
mov ax , [eax+1] ;
.nextdevice:
mov ebx, [esi]
cmp al, byte[device.pci_bus]
jne @f
cmp ah, byte[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
 
push edx
stdcall KernelAlloc, dword device.size ; Allocate the buffer for eth_device structure
pop edx
test eax, eax
jz .fail
mov ebx, eax ; ebx is always used as a pointer to the structure (in driver, but also in kernel code)
 
; Fill in the direct call addresses into the struct
 
mov [device.reset], reset
mov [device.transmit], transmit
mov [device.unload], unload
mov [device.name], my_service
 
; save the pci bus and device numbers
 
mov eax, [IOCTL.input]
movzx ecx, byte[eax+1]
mov [device.pci_bus], ecx
movzx ecx, byte[eax+2]
mov [device.pci_dev], ecx
 
; Now, it's time to find the base io addres of the PCI device
 
PCI_find_io
 
; We've found the io address, find IRQ now
 
PCI_find_irq
 
DEBUGF 2,"Hooking into device, dev:%x, bus:%x, irq:%x, addr:%x\n",\
[device.pci_dev]:1,[device.pci_bus]:1,[device.irq_line]:1,[device.io_addr]:8
 
allocate_and_clear [device.rx_p_des], RX_DES_COUNT*(DES.size+RX_BUFF_SIZE), .err
allocate_and_clear [device.tx_p_des], TX_DES_COUNT*(DES.size+TX_BUFF_SIZE), .err
 
; 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 .err2 ; If an error occured, exit
 
 
mov [device.type], NET_TYPE_ETH
call NetRegDev
 
cmp eax, -1
je .destroy
 
ret
 
; If the device was already loaded, find the device number and return it in eax
 
.find_devicenum:
DEBUGF 2,"Trying to find device number of already registered device\n"
call NetPtrToNum ; This kernel procedure converts a pointer to device struct in ebx
; into a device number in edi
mov eax, edi ; Application wants it in eax instead
DEBUGF 2,"Kernel says: %u\n", eax
ret
 
; If an error occured, remove all allocated data and exit (returning -1 in eax)
 
.destroy:
; todo: reset device into virgin state
 
.err2:
dec [devices]
.err:
DEBUGF 2,"removing device structure\n"
stdcall KernelFree, [device.rx_p_des]
stdcall KernelFree, [device.tx_p_des]
stdcall KernelFree, ebx
 
 
.fail:
or eax, -1
ret
 
;------------------------------------------------------
endp
 
 
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
;; ;;
;; Actual Hardware dependent code starts here ;;
;; ;;
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
 
 
 
align 4
unload:
; TODO: (in this particular order)
;
; - Stop the device
; - Detach int handler
; - Remove device from local list (RTL8139_LIST)
; - call unregister function in kernel
; - Remove all allocated structures and buffers the card used
 
or eax,-1
 
ret
 
 
macro status {
set_io CSR5
in eax, dx
DEBUGF 1,"CSR5: %x\n", eax
}
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Probe ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
probe:
 
DEBUGF 2,"Probing dec21x4x device: "
 
PCI_make_bus_master
 
stdcall PciRead32, [device.pci_bus], [device.pci_dev], 0 ; get device/vendor id
DEBUGF 1,"Vendor id: 0x%x\n", ax
 
cmp ax, 0x1011
je .dec
cmp ax, 0x1317
je .admtek
jmp .notfound
 
.dec:
shr eax, 16
DEBUGF 1,"Vendor ok!, device id: 0x%x\n", ax ; TODO: use another method to detect chip!
 
cmp ax, 0x0009
je .supported_device
 
cmp ax, 0x0019
je .supported_device2
 
.admtek:
shr eax, 16
DEBUGF 1,"Vendor ok!, device id: 0x%x\n", ax
 
cmp ax, 0x0985
je .supported_device
 
.notfound:
DEBUGF 1,"Device not supported!\n"
or eax, -1
ret
 
.supported_device2:
 
; wake up the 21143
 
xor eax, eax
stdcall PciWrite32, [device.pci_bus], [device.pci_dev], 0x40, eax
 
 
.supported_device:
call SROM_GetWidth ; TODO: use this value returned in ecx
; in the read_word routine!
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Reset ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
reset:
 
DEBUGF 2,"Resetting dec21x4x\n"
 
;-----------------------------------------------------------
; board software reset - if fails, dont do nothing else
 
set_io 0
status
set_io CSR0
mov eax, CSR0_RESET
out dx, eax
 
; wait at least 50 PCI cycles
mov esi, 1000
call Sleep
 
;-----------
; setup CSR0
 
set_io 0
status
set_io CSR0
mov eax, CSR0_DEFAULT
out dx, eax
 
 
; wait at least 50 PCI cycles
mov esi, 1000
call Sleep
 
;-----------------------------------
; Read mac from eeprom to driver ram
 
call read_mac_eeprom
 
;--------------------------------
; insert irq handler on given irq
 
movzx eax, [device.irq_line]
DEBUGF 1,"Attaching int handler to irq %x\n", eax:1
stdcall AttachIntHandler, eax, int_handler, dword 0
test eax, eax
jnz @f
DEBUGF 1,"\nCould not attach int handler!\n"
; or eax, -1
; ret
@@:
 
set_io 0
status
 
call init_ring
 
;--------------------------------------------
; setup CSR3 & CSR4 (pointers to descriptors)
 
set_io 0
status
set_io CSR3
mov eax, [device.rx_p_des]
GetRealAddr
DEBUGF 1,"RX descriptor base address: %x\n", eax
out dx, eax
 
set_io CSR4
mov eax, [device.tx_p_des]
GetRealAddr
DEBUGF 1,"TX descriptor base address: %x\n", eax
out dx, eax
 
;-------------------------------------------------------
; setup interrupt mask register -expect IRQs from now on
 
status
DEBUGF 1,"Enabling interrupts\n"
set_io CSR7
mov eax, CSR7_DEFAULT
out dx, eax
status
 
;----------
; enable RX
 
set_io 0
status
DEBUGF 1,"Enable RX\n"
 
set_io CSR6
Bit_Set CSR6_SR; or CSR6_PR or CSR6_ST
DEBUGF 1,"CSR6: %x\n", eax
 
status
 
call start_link
 
; wait a bit
mov esi, 3000
call Sleep
 
;----------------------------------------------------
; send setup packet to notify the board about the MAC
 
call Send_Setup_Packet
 
xor eax, eax
; clear packet/byte counters
 
lea edi, [device.bytes_tx]
mov ecx, 6
rep stosd
 
; Set the mtu, kernel will be able to send now
mov [device.mtu], 1514
 
; Set link state to unknown
mov [device.state], ETH_LINK_UNKOWN
 
DEBUGF 1,"Reset done\n"
 
ret
 
 
 
align 4
init_ring:
 
;------------------------------------------
; Setup RX descriptors (use chained method)
 
mov eax, [device.rx_p_des]
GetRealAddr
mov edx, eax
push eax
lea esi, [eax + RX_DES_COUNT*(DES.size)] ; jump over RX descriptors
mov eax, [device.rx_p_des]
add eax, RX_DES_COUNT*(DES.size) ; jump over RX descriptors
mov edi, [device.rx_p_des]
mov ecx, RX_DES_COUNT
.loop_rx_des:
add edx, DES.size
mov [edi + DES.status], DES0_OWN ; hardware owns buffer
mov [edi + DES.length], 1984 + RDES1_RCH ; only size of first buffer, chained buffers
mov [edi + DES.buffer1], esi ; hw buffer address
mov [edi + DES.buffer2], edx ; pointer to next descriptor
mov [edi + DES.virtaddr], eax ; virtual buffer address
DEBUGF 1,"RX desc: buff addr: %x, next desc: %x, real buff addr: %x, real descr addr: %x \n", esi, edx, eax, edi
 
add esi, RX_BUFF_SIZE
add eax, RX_BUFF_SIZE
add edi, DES.size
dec ecx
jnz .loop_rx_des
 
; set last descriptor as LAST
sub edi, DES.size
or [edi + DES.length], RDES1_RER ; EndOfRing
pop [edi + DES.buffer2] ; point it to the first descriptor
 
;---------------------
; Setup TX descriptors
 
mov eax, [device.tx_p_des]
GetRealAddr
mov edx, eax
push eax
lea esi, [eax + TX_DES_COUNT*(DES.size)] ; jump over TX descriptors
mov eax, [device.tx_p_des]
add eax, TX_DES_COUNT*(DES.size) ; jump over TX descriptors
mov edi, [device.tx_p_des]
mov ecx, TX_DES_COUNT
.loop_tx_des:
add edx, DES.size
mov [edi + DES.status], 0 ; owned by driver
mov [edi + DES.length], TDES1_TCH ; chained method
mov [edi + DES.buffer1], esi ; pointer to buffer
mov [edi + DES.buffer2], edx ; pointer to next descr
mov [edi + DES.virtaddr], eax
DEBUGF 1,"TX desc: buff addr: %x, next desc: %x, virt buff addr: %x, virt descr addr: %x \n", esi, edx, eax, edi
 
add esi, TX_BUFF_SIZE
add eax, TX_BUFF_SIZE
add edi, DES.size
dec ecx
jnz .loop_tx_des
; set last descriptor as LAST
sub edi, DES.size
or [edi + DES.length], TDES1_TER ; EndOfRing
pop [edi + DES.buffer2] ; point it to the first descriptor
 
;------------------
; Reset descriptors
 
mov [device.tx_wr_des], 0
mov [device.tx_rd_des], 0
mov [device.rx_crt_des], 0
mov [device.tx_free_des], TX_DES_COUNT
 
ret
 
 
align 4
start_link:
 
; TODO: write working code here
 
ret
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Send setup packet ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
Send_Setup_Packet:
 
DEBUGF 1,"Sending setup packet\n"
 
; if no descriptors available, out
mov ecx, 1000
@@loop_wait_desc:
cmp [device.tx_free_des], 0
jne @f
 
dec ecx
jnz @@loop_wait_desc
 
mov eax, -1
ret
@@:
 
; go to current send descriptor
mov edi, [device.tx_p_des]
mov eax, [device.tx_wr_des]
DEBUGF 1,"Got free descriptor: %u (%x)", eax, edi
mov edx, DES.size
mul edx
add edi, eax
DEBUGF 1,"=>%x\n", edi
 
; if NOT sending FIRST setup packet, must set current descriptor to 0 size for both buffers,
; and go to next descriptor for real setup packet... ;; TODO: check if 2 descriptors are available
 
; cmp [device.tx_packets], 0
; je .first
;
; and [edi+DES.des1], 0
; mov [edi+DES.des0], DES0_OWN
;
; go to next descriptor
; inc [device.tx_wr_des]
; and [device.tx_wr_des], TX_DES_COUNT-1
;
; dec free descriptors count
; cmp [device.tx_free_des], 0
; jz @f
; dec [device.tx_free_des]
; @@:
;
; ; recompute pointer to current descriptor
; mov edi, [device.tx_p_des]
; mov eax, [device.tx_wr_des]
; mov edx, DES.size
; mul edx
; add edi, eax
 
.first:
 
push edi
; copy setup packet to current descriptor
mov edi, [edi + DES.virtaddr]
; copy the address once
lea esi, [device.mac]
DEBUGF 1,"copying packet to %x from %x\n", edi, esi
mov ecx, 3 ; mac is 6 bytes thus 3 words
.loop:
DEBUGF 1,"%x ", [esi]:4
movsw
inc edi
inc edi
dec ecx
jnz .loop
 
DEBUGF 1,"\n"
 
; copy 15 times the broadcast address
mov ecx, 3*15
mov eax, 0xffffffff
rep stosd
 
pop edi
 
; setup descriptor
DEBUGF 1,"setting up descriptor\n"
mov [edi + DES.length], TDES1_IC + TDES1_SET + TDES1_TCH + 192 ; size must be EXACTLY 192 bytes
mov [edi + DES.status], DES0_OWN
 
DEBUGF 1,"status: %x\n", [edi + DES.status]:8
DEBUGF 1,"length: %x\n", [edi + DES.length]:8
DEBUGF 1,"buffer1: %x\n", [edi + DES.buffer1]:8
DEBUGF 1,"buffer2: %x\n", [edi + DES.buffer2]:8
 
; go to next descriptor
inc [device.tx_wr_des]
and [device.tx_wr_des], TX_DES_COUNT-1
 
; dec free descriptors count
cmp [device.tx_free_des], 0
jz @f
dec [device.tx_free_des]
@@:
 
; start tx
set_io 0
status
set_io CSR6
in eax, dx
test eax, CSR6_ST ; if NOT started, start now
jnz .already_started
or eax, CSR6_ST
DEBUGF 1,"Starting TX\n"
jmp .do_it
.already_started:
; if already started, issue a Transmit Poll command
set_io CSR1
xor eax, eax
DEBUGF 1,"Issuing transmit poll command\n"
.do_it:
out dx, eax
status
 
DEBUGF 1,"Sending setup packet, completed!\n"
 
ret
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Transmit ;;
;; ;;
;; In: buffer pointer in [esp+4] ;;
;; size of buffer in [esp+8] ;;
;; pointer to device structure in ebx ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
transmit:
 
DEBUGF 1,"Transmitting packet, buffer:%x, size:%u\n",[esp+4],[esp+8]
mov eax, [esp+4]
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 dword [esp+8], MAX_ETH_FRAME_SIZE
ja .fail
 
cmp [device.tx_free_des], 0
je .fail
 
;--------------------------
; copy packet to crt buffer
mov eax, [device.tx_wr_des]
mov edx, DES.size
mul edx
add eax, [device.tx_p_des]
mov edi, [eax + DES.virtaddr] ; pointer to buffer
mov esi, [esp+4]
mov ecx, [esp+8]
DEBUGF 1,"copying %u bytes from %x to %x\n", ecx, esi, edi
rep movsb
 
; set packet size
mov ecx, [eax+DES.length]
and ecx, TDES1_TER ; preserve 'End of Ring' bit
or ecx, [esp+8] ; set size
or ecx, TDES1_FS or TDES1_LS or TDES1_IC or TDES1_TCH ; first descr, last descr, interrupt on complete, chained modus
mov [eax+DES.length], ecx
 
; set descriptor info
mov [eax+DES.status], DES0_OWN ; say it is now owned by the 21x4x
 
; start tx
set_io 0
status
set_io CSR6
in eax, dx
test eax, CSR6_ST ; if NOT started, start now
jnz .already_started
or eax, CSR6_ST
DEBUGF 1,"Starting TX\n"
jmp .do_it
.already_started:
; if already started, issues a Transmit Poll command
set_io CSR1
mov eax, -1
.do_it:
out dx , eax
 
; Update stats
 
inc [device.packets_tx]
mov eax, [esp+8]
add dword [device.bytes_tx], eax
adc dword [device.bytes_tx + 4], 0
 
; go to next descriptor
inc [device.tx_wr_des]
and [device.tx_wr_des], TX_DES_COUNT-1
 
; dec free descriptors count
test [device.tx_free_des], -1
jz .end
dec [device.tx_free_des]
.end:
status
 
DEBUGF 1,"transmit ok\n"
xor eax, eax
stdcall KernelFree, [esp+4]
ret 8
 
.fail:
DEBUGF 1,"transmit failed\n"
or eax, -1
stdcall KernelFree, [esp+4]
ret 8
 
 
;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Interrupt handler ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
int_handler:
 
push ebx esi edi
 
DEBUGF 1,"\n%s int\n", my_service
 
; find pointer of device wich made IRQ occur
 
mov ecx, [devices]
test ecx, ecx
jz .nothing
mov esi, device_list
.nextdevice:
mov ebx, [esi]
 
set_io 0
set_io CSR5
in eax, dx
test eax, eax
jnz .got_it
out dx, eax ; send it back to ACK
.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,"Device: %x CSR5: %x ", ebx, ax
 
;----------------------------------
; TX ok?
 
test ax, CSR5_TI
jz .not_tx
push ax esi ecx
 
DEBUGF 1,"TX ok!\n"
; go to current descriptor
mov edi, [device.tx_p_des]
 
mov eax, [device.tx_rd_des]
mov edx, DES.size
mul edx
add edi, eax
.loop_tx:
; done if all desc are free
cmp [device.tx_free_des], TX_DES_COUNT
jz .end_tx
 
mov eax, [edi+DES.status]
 
; we stop at first desc that is owned be NIC
test eax, DES0_OWN
jnz .end_tx
 
; detect is setup packet
cmp eax, (0ffffffffh - DES0_OWN) ; all other bits are 1
jne .not_setup_packet
DEBUGF 1,"Setup Packet detected\n"
.not_setup_packet:
 
DEBUGF 1,"packet status: %x\n", eax
 
; next descriptor
add edi, DES.size
inc [device.tx_rd_des]
and [device.tx_rd_des], TX_DES_COUNT-1
 
; inc free desc
inc [device.tx_free_des]
cmp [device.tx_free_des], TX_DES_COUNT
jbe @f
mov [device.tx_free_des], TX_DES_COUNT
@@:
 
jmp .loop_tx
.end_tx:
;------------------------------------------------------
; here must be called standard Ethernet Tx Irq Handler
;------------------------------------------------------
 
pop ecx esi ax
 
;----------------------------------
; RX irq
.not_tx:
test ax, CSR5_RI
jz .not_rx
push ax esi ecx
 
DEBUGF 1,"RX ok!\n"
 
push ebx
.rx_loop:
pop ebx
 
; get current descriptor
mov edi, [device.rx_p_des]
mov eax, [device.rx_crt_des]
mov edx, DES.size
mul edx
add edi, eax
 
; now check status
mov eax, [edi + DES.status]
 
test eax, DES0_OWN
jnz .end_rx ; current desc is busy, nothing to do
 
test eax, RDES0_FS
jz .end_rx ; current desc is NOT first packet, ERROR!
 
test eax, RDES0_LS ; if not last desc of packet, error for now
jz .end_rx
 
test eax, RDES0_ES
jnz .end_rx
 
mov esi, [edi + DES.virtaddr]
mov ecx, [edi + DES.status]
shr ecx, RDES0_FL_SH
and ecx, RDES0_FL_MASK
sub ecx, 4 ; crc, we dont need it
 
DEBUGF 1,"Received packet!, size=%u, addr:%x\n", ecx, esi
 
push esi edi ecx
stdcall KernelAlloc, ecx ; Allocate a buffer to put packet into
pop ecx edi esi
test eax, eax
jz .fail
 
push ebx
push dword .rx_loop
push ecx eax
mov edi, eax
 
; update statistics
inc [device.packets_rx]
add dword [device.bytes_rx], ecx
adc dword [device.bytes_rx + 4], 0
 
; copy packet data
shr cx , 1
jnc .nb
movsb
.nb:
shr cx , 1
jnc .nw
movsw
.nw:
rep movsd
 
mov [edi + DES.status], DES0_OWN ; free descriptor
inc [device.rx_crt_des] ; next descriptor
and [device.rx_crt_des], RX_DES_COUNT-1
 
jmp Eth_input
 
.end_rx:
.fail:
pop ecx esi ax
.not_rx:
 
pop edi esi ebx
ret
 
 
 
align 4
write_mac: ; in: mac pushed onto stack (as 3 words)
 
DEBUGF 2,"Writing MAC: "
 
; write data into driver cache
mov esi, esp
lea edi, [device.mac]
movsd
movsw
add esp, 6
; send setup packet (only if driver is started)
call Send_Setup_Packet
 
align 4
read_mac:
 
DEBUGF 1,"Read_mac\n"
 
ret
 
 
 
align 4
read_mac_eeprom:
 
DEBUGF 1,"Read_mac_eeprom\n"
 
lea edi, [device.mac]
mov esi, 20/2 ; read words, start address is 20
.loop:
push esi edi
call SROM_Read_Word
pop edi esi
stosw
inc esi
cmp esi, 26/2
jb .loop
 
DEBUGF 2,"%x-%x-%x-%x-%x-%x\n",[edi-6]:2,[edi-5]:2,[edi-4]:2,[edi-3]:2,[edi-2]:2,[edi-1]:2
 
ret
 
align 4
write_mac_eeprom:
 
DEBUGF 1,"Write_mac_eeprom\n"
 
ret
 
 
align 4
SROM_GetWidth: ; should be 6 or 8 according to some manuals (returns in ecx)
 
DEBUGF 1,"SROM_GetWidth\n"
 
call SROM_Idle
call SROM_EnterAccessMode
 
; set_io 0
; set_io CSR9
 
; send 110b
 
in eax, dx
or eax, CSR9_SROM_DI
call SROM_out
 
in eax, dx
or eax, CSR9_SROM_DI
call SROM_out
 
in eax, dx
and eax, not (CSR9_SROM_DI)
call SROM_out
mov ecx,1
.loop2:
Bit_Set CSR9_SROM_CK
SROM_Delay
in eax, dx
and eax, CSR9_SROM_DO
jnz .not_zero
 
Bit_Clear CSR9_SROM_CK
SROM_Delay
jmp .end_loop2
.not_zero:
Bit_Clear CSR9_SROM_CK
SROM_Delay
inc ecx
cmp ecx, 12
jbe .loop2
.end_loop2:
DEBUGF 1,"Srom width=%u\n", ecx
call SROM_Idle
call SROM_EnterAccessMode
call SROM_Idle
ret
 
 
align 4
SROM_out:
 
out dx, eax
SROM_Delay
Bit_Set CSR9_SROM_CK
SROM_Delay
Bit_Clear CSR9_SROM_CK
SROM_Delay
 
ret
 
 
 
align 4
SROM_EnterAccessMode:
 
DEBUGF 1,"SROM_EnterAccessMode\n"
 
set_io 0
set_io CSR9
mov eax, CSR9_SR
out dx, eax
SROM_Delay
 
Bit_Set CSR9_RD
SROM_Delay
 
Bit_Clear CSR9_SROM_CK
SROM_Delay
 
Bit_Set CSR9_SROM_CS
SROM_Delay
ret
 
 
 
align 4
SROM_Idle:
 
DEBUGF 1,"SROM_Idle\n"
 
call SROM_EnterAccessMode
; set_io 0
; set_io CSR9
mov ecx, 25
.loop_clk:
 
Bit_Clear CSR9_SROM_CK
SROM_Delay
Bit_Set CSR9_SROM_CK
SROM_Delay
dec ecx
jnz .loop_clk
 
Bit_Clear CSR9_SROM_CK
SROM_Delay
Bit_Clear CSR9_SROM_CS
SROM_Delay
xor eax, eax
out dx, eax
ret
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Read serial EEprom word ;;
;; ;;
;; In: esi = read address ;;
;; OUT: ax = data word ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
align 4
SROM_Read_Word:
 
DEBUGF 1,"SROM_Read_word at: %x result: ", esi
 
set_io 0
set_io CSR9
 
; enter access mode
mov eax, CSR9_SR + CSR9_RD
out dx , eax
or eax, CSR9_SROM_CS
out dx , eax
 
; TODO: change this hard-coded 6-bit stuff to use value from srom_getwidth
; send read command "110b" + address to read from
and esi, 111111b
or esi, 110b shl 6
mov ecx, 1 shl 9
.loop_cmd:
mov eax, CSR9_SR + CSR9_RD + CSR9_SROM_CS
test esi, ecx
jz @f
or eax, CSR9_SROM_DI
@@:
out dx , eax
SROM_Delay
or eax, CSR9_SROM_CK
out dx , eax
SROM_Delay
shr ecx, 1
jnz .loop_cmd
 
; read data from SROM
 
xor esi, esi
mov ecx, 17 ;;; TODO: figure out why 17, not 16
.loop_read:
mov eax, CSR9_SR + CSR9_RD + CSR9_SROM_CS + CSR9_SROM_CK
out dx , eax
SROM_Delay
in eax, dx
and eax, CSR9_SROM_DO
shr eax, 3
shl esi, 1
or esi, eax
mov eax, CSR9_SR + CSR9_RD + CSR9_SROM_CS
out dx , eax
SROM_Delay
dec ecx
jnz .loop_read
mov eax, esi
 
DEBUGF 1,"%x\n", ax
 
ret
 
 
 
 
 
 
 
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<
 
 
 
;*********************************************************************
;* Media Descriptor Code *
;*********************************************************************
 
; MII transceiver control section.
; Read and write the MII registers using software-generated serial
; MDIO protocol. See the MII specifications or DP83840A data sheet
; for details.
 
; The maximum data clock rate is 2.5 Mhz. The minimum timing is usually
; met by back-to-back PCI I/O cycles, but we insert a delay to avoid
; "overclocking" issues or future 66Mhz PCI.
 
; Read and write the MII registers using software-generated serial
; MDIO protocol. It is just different enough from the EEPROM protocol
; to not share code. The maxium data clock rate is 2.5 Mhz.
 
MDIO_SHIFT_CLK = 0x10000
MDIO_DATA_WRITE0 = 0x00000
MDIO_DATA_WRITE1 = 0x20000
MDIO_ENB = 0x00000 ; Ignore the 0x02000 databook setting.
MDIO_ENB_IN = 0x40000
MDIO_DATA_READ = 0x80000
 
; MII transceiver control section.
; Read and write the MII registers using software-generated serial
; MDIO protocol. See the MII specifications or DP83840A data sheet
; for details.
 
align 4
mdio_read: ; phy_id:edx, location:esi
 
DEBUGF 1,"mdio read, phy=%x, location=%x", edx, esi
 
shl edx, 5
or esi, edx
or esi, 0xf6 shl 10
 
set_io 0
set_io CSR9
 
; if (tp->chip_id == LC82C168) {
; int i = 1000;
; outl(0x60020000 + (phy_id<<23) + (location<<18), ioaddr + 0xA0);
; inl(ioaddr + 0xA0);
; inl(ioaddr + 0xA0);
; while (--i > 0)
; if ( ! ((retval = inl(ioaddr + 0xA0)) & 0x80000000))
; return retval & 0xffff;
; return 0xffff;
; }
;
; if (tp->chip_id == COMET) {
; if (phy_id == 1) {
; if (location < 7)
; return inl(ioaddr + 0xB4 + (location<<2));
; else if (location == 17)
; return inl(ioaddr + 0xD0);
; else if (location >= 29 && location <= 31)
; return inl(ioaddr + 0xD4 + ((location-29)<<2));
; }
; return 0xffff;
; }
 
; Establish sync by sending at least 32 logic ones.
 
mov ecx, 32
.loop:
mov eax, MDIO_ENB or MDIO_DATA_WRITE1
out dx, eax
MDIO_Delay
 
or eax, MDIO_SHIFT_CLK
out dx, eax
MDIO_Delay
 
dec ecx
jnz .loop
 
 
; Shift the read command bits out.
 
mov ecx, 1 shl 15
.loop2:
mov eax, MDIO_ENB
test esi, ecx
jz @f
or eax, MDIO_DATA_WRITE1
@@:
out dx, eax
MDIO_Delay
 
or eax, MDIO_SHIFT_CLK
out dx, eax
MDIO_Delay
 
shr ecx, 1
jnz .loop2
 
 
; Read the two transition, 16 data, and wire-idle bits.
 
xor esi, esi
mov ecx, 19
.loop3:
mov eax, MDIO_ENB_IN
out dx, eax
MDIO_Delay
 
shl esi, 1
in eax, dx
test eax, MDIO_DATA_READ
jz @f
inc esi
@@:
 
mov eax, MDIO_ENB_IN or MDIO_SHIFT_CLK
out dx, eax
MDIO_Delay
 
dec ecx
jnz .loop3
 
shr esi, 1
movzx eax, si
 
DEBUGF 1,", data=%x\n", ax
 
ret
 
 
 
 
align 4
mdio_write: ;int phy_id: edx, int location: edi, int value: ax)
 
DEBUGF 1,"mdio write, phy=%x, location=%x, data=%x\n", edx, edi, ax
 
shl edi, 18
or edi, 0x5002 shl 16
shl edx, 23
or edi, edx
mov di, ax
 
set_io 0
set_io CSR9
 
; if (tp->chip_id == LC82C168) {
; int i = 1000;
; outl(cmd, ioaddr + 0xA0);
; do
; if ( ! (inl(ioaddr + 0xA0) & 0x80000000))
; break;
; while (--i > 0);
; return;
; }
 
; if (tp->chip_id == COMET) {
; if (phy_id != 1)
; return;
; if (location < 7)
; outl(value, ioaddr + 0xB4 + (location<<2));
; else if (location == 17)
; outl(value, ioaddr + 0xD0);
; else if (location >= 29 && location <= 31)
; outl(value, ioaddr + 0xD4 + ((location-29)<<2));
; return;
; }
 
 
; Establish sync by sending at least 32 logic ones.
 
mov ecx, 32
.loop:
mov eax, MDIO_ENB or MDIO_DATA_WRITE1
out dx, eax
MDIO_Delay
 
or eax, MDIO_SHIFT_CLK
out dx, eax
MDIO_Delay
 
dec ecx
jnz .loop
 
 
; Shift the command bits out.
 
mov ecx, 1 shl 31
.loop2:
mov eax, MDIO_ENB
test edi, ecx
jz @f
or eax, MDIO_DATA_WRITE1
@@:
out dx, eax
MDIO_Delay
 
or eax, MDIO_SHIFT_CLK
out dx, eax
MDIO_Delay
 
shr ecx, 1
jnz .loop2
 
 
; Clear out extra bits.
 
mov ecx, 2
.loop3:
mov eax, MDIO_ENB
out dx, eax
MDIO_Delay
 
or eax, MDIO_SHIFT_CLK
out dx, eax
MDIO_Delay
 
dec ecx
jnz .loop3
 
ret
 
 
; End of code
align 4 ; Place all initialised data here
 
devices dd 0
version dd (DRIVER_VERSION shl 16) or (API_VERSION and 0xFFFF)
my_service db 'DEC21X4X',0 ; max 16 chars include zero
 
include_debug_strings ; All data wich FDO uses will be included here
 
section '.data' data readable writable align 16 ; place all uninitialized data place here
 
device_list rd MAX_DEVICES ; This list contains all pointers to device structures the driver is handling
 
 
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
/drivers/ethernet/forcedeth.asm
0,0 → 1,1992
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; FORCEDETH.INC ;;
;; ;;
;; Ethernet driver for Kolibri OS ;;
;; ;;
;; Driver for chips of NVIDIA nForce2 ;;
;; References: ;;
;; forcedeth.c - linux driver (etherboot project) ;;
;; ethernet driver template by Mike Hibbett ;;
;; ;;
;; The copyright statement is ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;; Copyright 2008 shurf, ;;
;; cit.utc@gmail.com ;;
;; ;;
;; See file COPYING for details ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 
format MS COFF
 
API_VERSION = 0x01000100
DRIVER_VERSION = 5
 
MAX_DEVICES = 16
 
RBLEN = 0 ; Receive buffer size: 0=4K 1=8k 2=16k 3=32k 4=64k
; FIXME: option 1 and 2 will not allocate buffer correctly causing data loss!
 
DEBUG = 1
__DEBUG__ = 1
__DEBUG_LEVEL__ = 2
 
RX_RING = 4
TX_RING = 4
 
include '../proc32.inc'
include '../imports.inc'
include '../fdo.inc'
include '../struct.inc'
include '../netdrv.inc'
 
public START
public service_proc
public version
 
;**************************************************************************
; forcedeth Register Definitions
;**************************************************************************
 
PCI_DEVICE_ID_NVIDIA_NVENET_1 = 0x01c3
PCI_DEVICE_ID_NVIDIA_NVENET_2 = 0x0066
PCI_DEVICE_ID_NVIDIA_NVENET_4 = 0x0086
PCI_DEVICE_ID_NVIDIA_NVENET_5 = 0x008c
PCI_DEVICE_ID_NVIDIA_NVENET_3 = 0x00d6
PCI_DEVICE_ID_NVIDIA_NVENET_7 = 0x00df
PCI_DEVICE_ID_NVIDIA_NVENET_6 = 0x00e6
PCI_DEVICE_ID_NVIDIA_NVENET_8 = 0x0056
PCI_DEVICE_ID_NVIDIA_NVENET_9 = 0x0057
PCI_DEVICE_ID_NVIDIA_NVENET_10 = 0x0037
PCI_DEVICE_ID_NVIDIA_NVENET_11 = 0x0038
PCI_DEVICE_ID_NVIDIA_NVENET_12 = 0x0268
PCI_DEVICE_ID_NVIDIA_NVENET_13 = 0x0269
PCI_DEVICE_ID_NVIDIA_NVENET_14 = 0x0372
PCI_DEVICE_ID_NVIDIA_NVENET_15 = 0x0373
 
UNKSETUP1_VAL = 0x16070f
UNKSETUP2_VAL = 0x16
UNKSETUP3_VAL1 = 0x200010
UNKSETUP4_VAL = 8
UNKSETUP5_BIT31 = (1 shl 31)
UNKSETUP6_VAL = 3
 
TXRXCTL_RXCHECK = 0x0400
MIISTAT_ERROR = 0x0001
MIISTAT_MASK = 0x000f
MIISTAT_MASK2 = 0x000f
MIICTL_INUSE = 0x08000
MIICTL_WRITE = 0x00400
MIICTL_ADDRSHIFT = 5
 
MIISPEED_BIT8 = (1 shl 8)
MIIDELAY = 5
 
IRQ_RX_ERROR = 0x0001
IRQ_RX = 0x0002
IRQ_RX_NOBUF = 0x0004
IRQ_LINK = 0x0040
IRQ_TIMER = 0x0020
IRQMASK_WANTED_2 = 0x0147
 
IRQ_RX_ALL = IRQ_RX_ERROR or IRQ_RX or IRQ_RX_NOBUF
IRQ_TX_ALL = 0 ; ???????????
IRQ_OTHER_ALL = IRQ_LINK ;or IRQ_TIMER
 
IRQSTAT_MASK = 0x1ff
 
TXRXCTL_KICK = 0x0001
TXRXCTL_BIT1 = 0x0002
TXRXCTL_BIT2 = 0x0004
TXRXCTL_IDLE = 0x0008
TXRXCTL_RESET = 0x0010
TXRXCTL_RXCHECK = 0x0400
 
MCASTADDRA_FORCE = 0x01
 
MAC_RESET_ASSERT = 0x0F3
 
MISC1_HD = 0x02
MISC1_FORCE = 0x3b0f3c
 
PFF_ALWAYS = 0x7F0008
PFF_PROMISC = 0x80
PFF_MYADDR = 0x20
 
OFFLOAD_HOMEPHY = 0x601
OFFLOAD_NORMAL = 4096 shl RBLEN
 
RNDSEED_MASK = 0x00ff
RNDSEED_FORCE = 0x7f00
RNDSEED_FORCE2 = 0x2d00
RNDSEED_FORCE3 = 0x7400
 
; POLL_DEFAULT is the interval length of the timer source on the nic
; POLL_DEFAULT=97 would result in an interval length of 1 ms
POLL_DEFAULT = 970
 
ADAPTCTL_START = 0x02
ADAPTCTL_LINKUP = 0x04
ADAPTCTL_PHYVALID = 0x40000
ADAPTCTL_RUNNING = 0x100000
ADAPTCTL_PHYSHIFT = 24
 
WAKEUPFLAGS_VAL = 0x7770
 
POWERSTATE_POWEREDUP = 0x8000
POWERSTATE_VALID = 0x0100
POWERSTATE_MASK = 0x0003
POWERSTATE_D0 = 0x0000
POWERSTATE_D1 = 0x0001
POWERSTATE_D2 = 0x0002
POWERSTATE_D3 = 0x0003
 
POWERSTATE2_POWERUP_MASK = 0x0F11
POWERSTATE2_POWERUP_REV_A3= 0x0001
 
RCVCTL_START = 0x01
RCVSTAT_BUSY = 0x01
 
XMITCTL_START = 0x01
 
LINKSPEED_FORCE = 0x10000
LINKSPEED_10 = 1000
LINKSPEED_100 = 100
LINKSPEED_1000 = 50
 
RINGSZ_TXSHIFT = 0
RINGSZ_RXSHIFT = 16
 
LPA_1000FULL = 0x0800
 
; Link partner ability register.
LPA_SLCT = 0x001f ; Same as advertise selector
LPA_10HALF = 0x0020 ; Can do 10mbps half-duplex
LPA_10FULL = 0x0040 ; Can do 10mbps full-duplex
LPA_100HALF = 0x0080 ; Can do 100mbps half-duplex
LPA_100FULL = 0x0100 ; Can do 100mbps full-duplex
LPA_100BASE4 = 0x0200 ; Can do 100mbps 4k packets
LPA_RESV = 0x1c00 ; Unused...
LPA_RFAULT = 0x2000 ; Link partner faulted
LPA_LPACK = 0x4000 ; Link partner acked us
LPA_NPAGE = 0x8000 ; Next page bit
 
MII_READ = (-1)
MII_PHYSID1 = 0x02 ; PHYS ID 1
MII_PHYSID2 = 0x03 ; PHYS ID 2
MII_BMCR = 0x00 ; Basic mode control register
MII_BMSR = 0x01 ; Basic mode status register
MII_ADVERTISE = 0x04 ; Advertisement control reg
MII_LPA = 0x05 ; Link partner ability reg
MII_SREVISION = 0x16 ; Silicon revision
MII_RESV1 = 0x17 ; Reserved...
MII_NCONFIG = 0x1c ; Network interface config
 
; PHY defines
PHY_OUI_MARVELL = 0x5043
PHY_OUI_CICADA = 0x03f1
PHYID1_OUI_MASK = 0x03ff
PHYID1_OUI_SHFT = 6
PHYID2_OUI_MASK = 0xfc00
PHYID2_OUI_SHFT = 10
PHY_INIT1 = 0x0f000
PHY_INIT2 = 0x0e00
PHY_INIT3 = 0x01000
PHY_INIT4 = 0x0200
PHY_INIT5 = 0x0004
PHY_INIT6 = 0x02000
PHY_GIGABIT = 0x0100
 
PHY_TIMEOUT = 0x1
PHY_ERROR = 0x2
 
PHY_100 = 0x1
PHY_1000 = 0x2
PHY_HALF = 0x100
 
PHY_RGMII = 0x10000000
 
; desc_ver values:
; This field has two purposes:
; - Newer nics uses a different ring layout. The layout is selected by
; comparing np->desc_ver with DESC_VER_xy.
; - It contains bits that are forced on when writing to TxRxControl.
DESC_VER_1 = 0x0
DESC_VER_2 = (0x02100 or TXRXCTL_RXCHECK)
 
MAC_ADDR_LEN = 6
 
NV_TX_LASTPACKET = (1 shl 16)
NV_TX_RETRYERROR = (1 shl 19)
NV_TX_LASTPACKET1 = (1 shl 24)
NV_TX_DEFERRED = (1 shl 26)
NV_TX_CARRIERLOST = (1 shl 27)
NV_TX_LATECOLLISION = (1 shl 28)
NV_TX_UNDERFLOW = (1 shl 29)
NV_TX_ERROR = (1 shl 30)
NV_TX_VALID = (1 shl 31)
 
NV_TX2_LASTPACKET = (1 shl 29)
NV_TX2_RETRYERROR = (1 shl 18)
NV_TX2_LASTPACKET1 = (1 shl 23)
NV_TX2_DEFERRED = (1 shl 25)
NV_TX2_CARRIERLOST = (1 shl 26)
NV_TX2_LATECOLLISION = (1 shl 27)
NV_TX2_UNDERFLOW = (1 shl 28)
; error and valid are the same for both
NV_TX2_ERROR = (1 shl 30)
NV_TX2_VALID = (1 shl 31)
 
NV_RX_DESCRIPTORVALID = (1 shl 16)
NV_RX_AVAIL = (1 shl 31)
 
NV_RX2_DESCRIPTORVALID = (1 shl 29)
 
FLAG_MASK_V1 = 0xffff0000
FLAG_MASK_V2 = 0xffffc000
LEN_MASK_V1 = (0xffffffff xor FLAG_MASK_V1)
LEN_MASK_V2 = (0xffffffff xor FLAG_MASK_V2)
 
; Miscelaneous hardware related defines:
NV_PCI_REGSZ_VER1 = 0x270
NV_PCI_REGSZ_VER2 = 0x604
; various timeout delays: all in usec
NV_TXRX_RESET_DELAY = 4
NV_TXSTOP_DELAY1 = 10
NV_TXSTOP_DELAY1MAX = 500000
NV_TXSTOP_DELAY2 = 100
NV_RXSTOP_DELAY1 = 10
NV_RXSTOP_DELAY1MAX = 500000
NV_RXSTOP_DELAY2 = 100
NV_SETUP5_DELAY = 5
NV_SETUP5_DELAYMAX = 50000
NV_POWERUP_DELAY = 5
NV_POWERUP_DELAYMAX = 5000
NV_MIIBUSY_DELAY = 50
NV_MIIPHY_DELAY = 10
NV_MIIPHY_DELAYMAX = 10000
NV_MAC_RESET_DELAY = 64
NV_WAKEUPPATTERNS = 5
NV_WAKEUPMASKENTRIES = 4
 
; Advertisement control register.
ADVERTISE_SLCT = 0x001f ; Selector bits
ADVERTISE_CSMA = 0x0001 ; Only selector supported
ADVERTISE_10HALF = 0x0020 ; Try for 10mbps half-duplex
ADVERTISE_10FULL = 0x0040 ; Try for 10mbps full-duplex
ADVERTISE_100HALF = 0x0080 ; Try for 100mbps half-duplex
ADVERTISE_100FULL = 0x0100 ; Try for 100mbps full-duplex
ADVERTISE_100BASE4 = 0x0200 ; Try for 100mbps 4k packets
ADVERTISE_RESV = 0x1c00 ; Unused...
ADVERTISE_RFAULT = 0x2000 ; Say we can detect faults
ADVERTISE_LPACK = 0x4000 ; Ack link partners response
ADVERTISE_NPAGE = 0x8000 ; Next page bit
 
ADVERTISE_FULL = (ADVERTISE_100FULL or ADVERTISE_10FULL or ADVERTISE_CSMA)
ADVERTISE_ALL = (ADVERTISE_10HALF or ADVERTISE_10FULL or ADVERTISE_100HALF or ADVERTISE_100FULL)
 
MII_1000BT_CR = 0x09
MII_1000BT_SR = 0x0a
ADVERTISE_1000FULL = 0x0200
ADVERTISE_1000HALF = 0x0100
 
BMCR_ANRESTART = 0x0200 ; Auto negotiation restart
BMCR_ANENABLE = 0x1000 ; Enable auto negotiation
BMCR_SPEED100 = 0x2000 ; Select 100Mbps
BMCR_LOOPBACK = 0x4000 ; TXD loopback bits
BMCR_RESET = 0x8000 ; Reset the DP83840
 
; Basic mode status register.
BMSR_ERCAP = 0x0001 ; Ext-reg capability
BMSR_JCD = 0x0002 ; Jabber detected
BMSR_LSTATUS = 0x0004 ; Link status
BMSR_ANEGCAPABLE = 0x0008 ; Able to do auto-negotiation
BMSR_RFAULT = 0x0010 ; Remote fault detected
BMSR_ANEGCOMPLETE = 0x0020 ; Auto-negotiation complete
BMSR_RESV = 0x07c0 ; Unused...
BMSR_10HALF = 0x0800 ; Can do 10mbps, half-duplex
BMSR_10FULL = 0x1000 ; Can do 10mbps, full-duplex
BMSR_100HALF = 0x2000 ; Can do 100mbps, half-duplex
BMSR_100FULL = 0x4000 ; Can do 100mbps, full-duplex
BMSR_100BASE4 = 0x8000 ; Can do 100mbps, 4k packets
 
struct TxDesc
PacketBuffer dd ?
FlagLen dd ?
ends
 
struct RxDesc
PacketBuffer dd ?
FlagLen dd ?
ends
 
virtual at ebx
 
device:
 
ETH_DEVICE
 
.pci_bus dd ?
.pci_dev dd ?
 
.mmio_addr dd ?
.vendor_id dw ?
.device_id dw ?
.txflags dd ?
.desc_ver dd ?
.irqmask dd ?
.wolenabled dd ?
.in_shutdown dd ?
.cur_rx dd ?
.phyaddr dd ?
.phy_oui dd ?
.gigabit dd ?
.needs_mac_reset dd ?
.linkspeed dd ?
.duplex dd ?
.next_tx dd ?
.nocable dd ?
 
rb 0x100 - (($ - device) and 0xff)
.tx_ring rd (TX_RING * sizeof.TxDesc) /4*2
 
rb 0x100 - (($ - device) and 0xff)
.rx_ring rd (RX_RING * sizeof.RxDesc) /4*2
 
sizeof.device_struct = $ - device
end virtual
 
 
virtual at edi
IrqStatus dd ?
IrqMask dd ?
UnknownSetupReg6 dd ?
PollingInterval dd ?
end virtual
 
virtual at edi + 0x3c
MacReset dd ?
end virtual
 
virtual at edi + 0x80
Misc1 dd ?
TransmitterControl dd ?
TransmitterStatus dd ?
PacketFilterFlags dd ?
OffloadConfig dd ?
ReceiverControl dd ?
ReceiverStatus dd ?
RandomSeed dd ?
UnknownSetupReg1 dd ?
UnknownSetupReg2 dd ?
MacAddrA dd ?
MacAddrB dd ?
MulticastAddrA dd ?
MulticastAddrB dd ?
MulticastMaskA dd ?
MulticastMaskB dd ?
PhyInterface dd ?
end virtual
 
virtual at edi + 0x100
TxRingPhysAddr dd ?
RxRingPhysAddr dd ?
RingSizes dd ?
UnknownTransmitterReg dd ?
LinkSpeed dd ?
end virtual
 
virtual at edi + 0x130
UnknownSetupReg5 dd ?
end virtual
 
virtual at edi + 0x13c
UnknownSetupReg3 dd ?
end virtual
 
virtual at edi + 0x144
TxRxControl dd ?
end virtual
 
virtual at edi + 0x180
MIIStatus dd ?
UnknownSetupReg4 dd ?
AdapterControl dd ?
MIISpeed dd ?
MIIControl dd ?
MIIData dd ?
end virtual
 
virtual at edi + 0x200
WakeUpFlags dd ?
end virtual
 
virtual at edi + 0x26c
PowerState dd ?
end virtual
 
virtual at edi + 0x600
PowerState2 dd ?
end virtual
 
 
section '.flat' code readable align 16
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; proc START ;;
;; ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
proc START stdcall, state:dword
 
cmp [state], 1
jne .exit
 
DEBUGF 2,"Loading %s driver\n", my_service
stdcall RegService, my_service, service_proc
ret
 
.exit:
xor eax, eax
ret
 
endp
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; proc SERVICE_PROC ;;
;; ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
proc service_proc stdcall, ioctl:dword
 
mov edx, [ioctl]
mov eax, [IOCTL.io_code]
 
;------------------------------------------------------
 
cmp eax, 0 ;SRV_GETVERSION
jne @F
 
cmp [IOCTL.out_size], 4
jb .fail
mov eax, [IOCTL.output]
mov [eax], dword API_VERSION
 
xor eax, eax
ret
 
;------------------------------------------------------
@@:
cmp eax, 1 ;SRV_HOOK
jne .fail
 
cmp [IOCTL.inp_size], 3 ; Data input must be at least 3 bytes
jb .fail
 
mov eax, [IOCTL.input]
cmp byte [eax], 1 ; 1 means device number and bus number (pci) are given
jne .fail ; other types arent supported for this card yet
 
; check if the device is already listed
 
mov esi, device_list
mov ecx, [devices]
test ecx, ecx
jz .firstdevice
 
; mov eax, [IOCTL.input] ; get the pci bus and device numbers
mov ax, [eax+1]
.nextdevice:
mov ebx, [esi]
cmp al, byte [device.pci_bus] ; compare with pci and device num in device list (notice the usage of word instead of byte)
jne @f
cmp ah, byte [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_struct, .fail ; Allocate the buffer for device structure
 
; Fill in the direct call addresses into the struct
 
mov [device.reset], reset
mov [device.transmit], transmit
mov [device.unload], .fail
mov [device.name], my_service
 
; save the pci bus and device numbers
 
mov eax, [IOCTL.input]
movzx ecx, byte [eax+1]
mov [device.pci_bus], ecx
movzx ecx, byte [eax+2]
mov [device.pci_dev], ecx
 
DEBUGF 1,"Hooking into device, dev:%x, bus:%x\n", [device.pci_dev], [device.pci_bus]
 
; Ok, the eth_device structure is ready, let's probe the device
call probe ; this function will output in eax
test eax, eax
jnz .err ; If an error occured, exit
 
mov eax, [devices] ; Add the device structure to our device list
mov [device_list+4*eax], ebx ; (IRQ handler uses this list to find device)
inc [devices] ;
 
mov [device.type], NET_TYPE_ETH
call 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"
call NetPtrToNum ; This kernel procedure converts a pointer to device struct in ebx
; into a device number in edi
mov eax, edi ; Application wants it in eax instead
DEBUGF 1,"Kernel says: %u\n", eax
ret
 
; If an error occured, remove all allocated data and exit (returning -1 in eax)
 
.destroy:
; todo: reset device into virgin state
 
.err:
stdcall KernelFree, ebx
.fail:
 
ret
 
;------------------------------------------------------
endp
 
 
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
;; ;;
;; Actual Hardware dependent code starts here ;;
;; ;;
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
 
 
 
 
;***************************************************************************
; Function
; probe
; Description
; Searches for an ethernet card, enables it and clears the rx buffer
;
;***************************************************************************
align 4
probe:
 
DEBUGF 1,"probe\n"
 
mov [device.needs_mac_reset], 0
 
PCI_make_bus_master
PCI_adjust_latency 32
PCI_find_mmio32
 
DEBUGF 1,"mmio_addr= 0x%x\n", [device.mmio_addr]:8
 
stdcall MapIoMem, [device.mmio_addr], 2048, (PG_SW + PG_NOCACHE)
test eax, eax
jz fail
mov [device.mmio_addr], eax
mov edi, eax
 
DEBUGF 1,"mapped mmio_addr= 0x%x\n", [device.mmio_addr]:8
 
;-------------------------------------
; handle different descriptor versions
mov [device.desc_ver], DESC_VER_1
movzx eax, [device.device_id]
cmp eax, PCI_DEVICE_ID_NVIDIA_NVENET_1
je .ver1
cmp eax, PCI_DEVICE_ID_NVIDIA_NVENET_2
je .ver1
cmp eax, PCI_DEVICE_ID_NVIDIA_NVENET_3
je .ver1
mov [device.desc_ver], DESC_VER_2
.ver1:
 
call read_mac
 
; disable WOL
mov [WakeUpFlags], 0
mov [device.wolenabled], 0
mov [device.txflags], (NV_TX2_LASTPACKET or NV_TX2_VALID)
cmp [device.desc_ver], DESC_VER_1
jne @f
mov [device.txflags], (NV_TX_LASTPACKET or NV_TX_VALID)
@@:
 
; BEGIN of switch (pci->dev_id)
 
cmp [device.device_id], 0x01C3
jne .next_0x0066
; nforce
mov [device.irqmask], 0 ;;;;;;;;;;;;;;;(IRQMASK_WANTED_2 or IRQ_TIMER)
jmp .find_phy
 
.next_0x0066:
cmp [device.device_id], 0x0066
je @f
cmp [device.device_id], 0x00D6
je @f
jmp .next_0x0086
@@:
mov [device.irqmask], 0 ;;;;;;;;;;;;;;;;(IRQMASK_WANTED_2 or IRQ_TIMER)
cmp [device.desc_ver], DESC_VER_1
jne @f
or [device.txflags], NV_TX_LASTPACKET1
jmp .find_phy
@@:
or [device.txflags], NV_TX2_LASTPACKET1
jmp .find_phy
 
.next_0x0086:
cmp [device.device_id], 0x0086
je @f
cmp [device.device_id], 0x008c
je @f
cmp [device.device_id], 0x00e6
je @f
cmp [device.device_id], 0x00df
je @f
cmp [device.device_id], 0x0056
je @f
cmp [device.device_id], 0x0057
je @f
cmp [device.device_id], 0x0037
je @f
cmp [device.device_id], 0x0038
je @f
jmp .find_phy
 
@@:
mov [device.irqmask], 0 ;;;;;;;;;;;;;;;;(IRQMASK_WANTED_2 or IRQ_TIMER)
 
cmp [device.desc_ver], DESC_VER_1
jne @f
or [device.txflags], NV_TX_LASTPACKET1
jmp .find_phy
@@:
or [device.txflags], NV_TX2_LASTPACKET1
jmp .find_phy
 
.next_0x0268:
; cmp word [device_id], 0x0268
; je @f
; cmp word [device_id], 0x0269
; je @f
; cmp word [device_id], 0x0372
; je @f
; cmp word [device_id], 0x0373
; je @f
; jmp .default_switch
;@@:
cmp [device.device_id], 0x0268
jb .undefined
 
; Get device revision
 
stdcall PciRead8, [device.pci_bus], [device.pci_dev], PCI_REVISION_ID
 
; take phy and nic out of low power mode
mov ecx, [PowerState2]
and ecx, not POWERSTATE2_POWERUP_MASK
cmp [device.device_id], PCI_DEVICE_ID_NVIDIA_NVENET_12
jne @f
cmp [device.device_id], PCI_DEVICE_ID_NVIDIA_NVENET_13
jne @f
cmp al, 0xA3
jb @f
or ecx, POWERSTATE2_POWERUP_REV_A3
@@:
mov [PowerState2], ecx
 
; DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ
mov [device.irqmask], 0 ;;;;;;;;;;;;;;;;(IRQMASK_WANTED_2 or IRQ_TIMER)
mov [device.needs_mac_reset], 1
cmp [device.desc_ver], DESC_VER_1
jne @f
or [device.txflags], NV_TX_LASTPACKET1
jmp .find_phy
@@:
cmp [device.desc_ver], DESC_VER_2
jne .undefined
or [device.txflags], NV_TX2_LASTPACKET1
jmp .find_phy
 
.undefined:
DEBUGF 1,"Your card was undefined in this driver.\n"
DEBUGF 1,"Review driver_data in Kolibri driver and send a patch\n"
 
; Find a suitable phy
; Start with address 1 to 31, then do 0, then fail
 
.find_phy:
xor edx, edx
.phy_loop:
inc edx
and edx, 0x1f ; phyaddr = i & 0x1f
mov eax, MII_PHYSID1
mov ecx, MII_READ
call mii_rw ; EDX - addr, EAX - miireg, ECX - value
 
cmp eax, 0xffff
je .try_next
cmp eax, 0
jl .try_next
mov esi, eax
 
mov eax, MII_PHYSID2
mov ecx, MII_READ
call mii_rw
 
cmp eax, 0xffff
je .try_next
cmp eax, 0
jl .try_next
jmp .got_it
 
.try_next:
test edx, edx
jnz .phy_loop
 
; PHY in isolate mode? No phy attached and user wants to test loopback?
; Very odd, but can be correct.
DEBUGF 1,"Could not find a valid PHY.\n"
jmp .no_phy
 
.got_it:
 
and esi, PHYID1_OUI_MASK
shl esi, PHYID1_OUI_SHFT
 
and eax, PHYID2_OUI_MASK
shr eax, PHYID2_OUI_SHFT
 
DEBUGF 1,"Found PHY 0x%x:0x%x at address 0x%x\n", esi:8, eax:8, edx
 
mov [device.phyaddr], edx
or eax, esi
mov [device.phy_oui], eax
 
call phy_init
 
.no_phy:
 
cmp [device.needs_mac_reset], 0
je @f
call mac_reset
@@:
;***************************************************************************
; Function
; reset
; Description
; Place the chip (ie, the ethernet card) into a virgin state
; No inputs
; All registers destroyed
;
;***************************************************************************
reset:
 
DEBUGF 1,"Resetting\n"
 
stdcall PciRead8, [device.pci_bus], [device.pci_dev], PCI_REG_IRQ
movzx eax, al
stdcall AttachIntHandler, eax, int_handler, dword 0
test eax, eax
jnz @f
DEBUGF 1,"\nCould not attach int handler!\n"
; or eax, -1
; ret
@@:
 
; erase previous misconfiguration
 
mov edi, [device.mmio_addr]
mov [MulticastAddrA], MCASTADDRA_FORCE
mov [MulticastAddrB], 0
mov [MulticastMaskA], 0
mov [MulticastMaskB], 0
mov [PacketFilterFlags], 0
mov [TransmitterControl], 0
mov [ReceiverControl], 0
mov [AdapterControl], 0
 
; initialize descriptor rings
 
call init_ring
 
mov [LinkSpeed], 0
mov [UnknownTransmitterReg], 0
 
call txrx_reset
mov [UnknownSetupReg6], 0
mov [device.in_shutdown], 0
 
; give hw rings
 
lea eax, [device.rx_ring]
GetRealAddr
mov [RxRingPhysAddr], eax
 
lea eax, [device.tx_ring]
GetRealAddr
mov [TxRingPhysAddr], eax
 
mov [RingSizes], (((RX_RING - 1) shl RINGSZ_RXSHIFT) + ((TX_RING - 1) shl RINGSZ_TXSHIFT))
 
;
 
mov [device.linkspeed], (LINKSPEED_FORCE or LINKSPEED_10)
mov [device.duplex], 0
mov [LinkSpeed], (LINKSPEED_FORCE or LINKSPEED_10)
mov [UnknownSetupReg3], UNKSETUP3_VAL1
 
mov eax, [device.desc_ver]
mov [TxRxControl], eax
call pci_push
or eax, TXRXCTL_BIT1
mov [TxRxControl], eax
 
stdcall reg_delay, UnknownSetupReg5-edi, UNKSETUP5_BIT31, UNKSETUP5_BIT31, NV_SETUP5_DELAY, NV_SETUP5_DELAYMAX, 0
 
mov [UnknownSetupReg4], 0
mov [MIIStatus], MIISTAT_MASK2
 
;
mov [Misc1], (MISC1_FORCE or MISC1_HD)
 
mov eax, [TransmitterStatus]
mov [TransmitterStatus], eax
 
mov [PacketFilterFlags], PFF_ALWAYS
 
mov [OffloadConfig], OFFLOAD_NORMAL
 
mov eax, [ReceiverStatus]
mov [ReceiverStatus], eax
 
; set random seed
push ebx
stdcall GetTimerTicks ; bad idea, driver is started at system startup in 90% of cases..
pop ebx
 
mov edi, [device.mmio_addr]
 
and eax, RNDSEED_MASK
or eax, RNDSEED_FORCE
mov [RandomSeed], eax
 
mov [UnknownSetupReg1], UNKSETUP1_VAL
mov [UnknownSetupReg2], UNKSETUP2_VAL
mov [PollingInterval], POLL_DEFAULT
mov [UnknownSetupReg6], UNKSETUP6_VAL
 
mov eax, [device.phyaddr]
shl eax, ADAPTCTL_PHYSHIFT
or eax, (ADAPTCTL_PHYVALID or ADAPTCTL_RUNNING)
mov [AdapterControl], eax
 
mov [MIISpeed], (MIISPEED_BIT8 or MIIDELAY)
mov [UnknownSetupReg4], UNKSETUP4_VAL
mov [WakeUpFlags], WAKEUPFLAGS_VAL
or [PowerState], POWERSTATE_POWEREDUP
call pci_push
 
mov esi, 10
call Sleep
 
or [PowerState], POWERSTATE_VALID
mov [IrqMask], 0
 
;;; ; ??? Mask RX interrupts
mov [IrqMask], IRQ_RX_ALL + IRQ_TX_ALL
;;; ; ??? Mask TX interrupts
;;; mov [IrqMask], IRQ_TX_ALL
;;; ; ??? Mask OTHER interrupts
;;; mov [IrqMask], IRQ_OTHER_ALL
call pci_push
 
mov [MIIStatus], MIISTAT_MASK2
mov [IrqStatus], IRQSTAT_MASK
call pci_push
 
mov [MulticastAddrA], MCASTADDRA_FORCE
mov [MulticastAddrB], 0
mov [MulticastMaskA], 0
mov [MulticastMaskB], 0
 
mov [PacketFilterFlags], (PFF_ALWAYS or PFF_MYADDR)
 
call set_multicast
; One manual link speed update: Interrupts are enabled, future link
; speed changes cause interrupts and are handled by nv_link_irq().
 
mov eax, [MIIStatus]
mov [MIIStatus], MIISTAT_MASK
DEBUGF 1,"startup: got 0x%x\n", eax
 
call update_linkspeed
 
mov [TransmitterControl], XMITCTL_START ; start TX
call pci_push
 
mov [device.nocable], 0
test eax, eax
jnz .return
DEBUGF 1,"no link during initialization.\n"
mov [device.nocable], 1
 
.return:
xor eax, eax ; Indicate that we have successfully reset the card
mov [device.mtu], 1514 ;;; FIXME
 
; Set link state to unknown
mov [device.state], ETH_LINK_UNKOWN
 
ret
 
 
fail:
or eax, -1
 
ret
 
;--------------------------------------------------------
;
; MII_RW
;
; read/write a register on the PHY.
; Caller must guarantee serialization
; Input: EAX - miireg, EDX - addr, ECX - value
; Output: EAX - retval
;
;--------------------------------------------------------
 
mii_rw:
 
DEBUGF 1,"mii_rw: 0x%x to reg %d at PHY %d\n", ecx, eax, edx
 
push edx
 
mov edi, [device.mmio_addr]
mov [MIIStatus], MIISTAT_MASK
 
test [MIIControl], MIICTL_INUSE
jz @f
mov [MIIControl], MIICTL_INUSE
 
mov esi, NV_MIIBUSY_DELAY
call Sleep
@@:
 
shl edx, MIICTL_ADDRSHIFT
or edx, eax
 
cmp ecx, MII_READ
je @f
 
mov [MIIData], ecx
or edx, MIICTL_WRITE
@@:
mov [MIIControl], edx
 
stdcall reg_delay, MIIControl-edi, MIICTL_INUSE, 0, NV_MIIPHY_DELAY, NV_MIIPHY_DELAYMAX, 0
 
test eax, eax
jz @f
DEBUGF 1,"mii_rw timed out.\n"
or eax, -1
jmp .return
@@:
 
cmp ecx, MII_READ
je @f
; it was a write operation - fewer failures are detectable
DEBUGF 1,"mii_rw write: ok\n"
xor eax, eax
jmp .return
@@:
 
mov eax, [MIIStatus]
test eax, MIISTAT_ERROR
jz @f
DEBUGF 1,"mii read: failed.\n"
or eax, -1
jmp .return
@@:
 
mov eax, [MIIData]
DEBUGF 1,"mii read: 0x%x.\n", eax
 
.return:
pop edx
ret
 
 
 
 
 
; Input: offset:word, mask:dword, target:dword, delay:word, delaymax:word, msg:dword
; Output: EAX - 0|1
 
proc reg_delay, offset:dword, mask:dword, target:dword, delay:dword, delaymax:dword, msg:dword
 
; DEBUGF 1,"reg_delay\n"
 
push esi
call pci_push
 
.loop:
mov esi, [delay]
call Sleep
mov eax, [delaymax]
sub eax, [delay]
mov [delaymax], eax
 
cmp eax, 0
jl .fail
 
mov eax, [offset]
mov eax, [edi + eax]
and eax, [mask]
cmp eax, [target]
jne .loop
 
pop esi
xor eax, eax
ret
 
.fail:
pop esi
xor eax, eax
inc eax
ret
 
endp
 
 
 
 
 
; Input: none
; Output: EAX - result (0 = OK, other = error)
phy_init:
 
push ebx ecx
; set advertise register
mov edx, [device.phyaddr]
mov eax, MII_ADVERTISE
mov ecx, MII_READ
call mii_rw
 
or eax, (ADVERTISE_10HALF or ADVERTISE_10FULL or ADVERTISE_100HALF or ADVERTISE_100FULL or 0x800 or 0x400)
 
mov ecx, eax
mov eax, MII_ADVERTISE
call mii_rw
 
test eax, eax
jz @f
 
DEBUGF 1,"phy write to advertise failed.\n"
 
mov eax, PHY_ERROR
jmp .return
@@:
 
; get phy interface type
mov edi, [device.mmio_addr]
mov eax, [PhyInterface]
DEBUGF 1,"phy interface type = 0x%x\n", eax:8
 
; see if gigabit phy
mov eax, MII_BMSR
mov ecx, MII_READ
call mii_rw
test eax, PHY_GIGABIT
jnz .gigabit
mov [device.gigabit], 0
jmp .next_if
 
.gigabit:
mov [device.gigabit], PHY_GIGABIT
 
mov eax, MII_1000BT_CR
mov ecx, MII_READ
call mii_rw
and eax, (not ADVERTISE_1000HALF)
 
test [PhyInterface], PHY_RGMII
jz @f
or eax, ADVERTISE_1000FULL
jmp .next
@@:
 
and eax, (not ADVERTISE_1000FULL)
 
.next:
mov ecx, eax
mov eax, MII_1000BT_CR
call mii_rw
 
test eax, eax
jz .next_if
 
DEBUGF 1,"phy init failed.\n"
 
mov eax, PHY_ERROR
jmp .return
 
.next_if:
 
call phy_reset
test eax, eax
jz @f
 
DEBUGF 1,"phy reset failed.\n"
 
mov eax, PHY_ERROR
jmp .return
@@:
 
; phy vendor specific configuration
cmp [device.phy_oui], PHY_OUI_CICADA
jne .next_if2
test [PhyInterface], PHY_RGMII
jz .next_if2
 
mov eax, MII_RESV1
mov ecx, MII_READ
call mii_rw
 
and eax, (not (PHY_INIT1 or PHY_INIT2))
or eax, (PHY_INIT3 or PHY_INIT4)
mov ecx, eax
mov eax, MII_RESV1
call mii_rw
 
test eax, eax
jz @f
 
DEBUGF 1,"phy init failed.\n"
 
mov eax, PHY_ERROR
jmp .return
@@:
 
mov eax, MII_NCONFIG
mov ecx, MII_READ
call mii_rw
 
or eax, PHY_INIT5
mov ecx, eax
mov eax, MII_NCONFIG
call mii_rw
test eax, eax
jz .next_if2
 
DEBUGF 1,"phy init failed.\n"
 
mov eax, PHY_ERROR
jmp .return
 
 
 
.next_if2:
 
cmp [device.phy_oui], PHY_OUI_CICADA
jne .restart
mov eax, MII_SREVISION
mov ecx, MII_READ
call mii_rw
or eax, PHY_INIT6
mov ecx, eax
mov eax, MII_SREVISION
call mii_rw
test eax, eax
jz .restart
 
DEBUGF 1,"phy init failed.\n"
 
jmp .return
 
.restart:
; restart auto negotiation
 
mov eax, MII_BMCR
mov ecx, MII_READ
call mii_rw
 
or eax, (BMCR_ANRESTART or BMCR_ANENABLE)
mov ecx, eax
mov eax, MII_BMCR
call mii_rw
test eax, eax
jz .ok
 
mov eax, PHY_ERROR
jmp .return
 
.ok:
xor eax, eax
.return:
pop ecx ebx
 
ret
 
 
; Input: none
; Output: EAX - result (0 = OK, other = error)
phy_reset:
 
DEBUGF 1,"phy_reset\n"
 
push ebx ecx edx
 
mov edx, [device.phyaddr]
mov eax, MII_BMCR
mov ecx, MII_READ
call mii_rw
 
or eax, BMCR_RESET
push eax
mov ecx, eax
mov eax, MII_BMCR
call mii_rw
 
test eax, eax
jz @f
 
pop eax
mov eax, 0xffffffff
jmp .return
@@:
 
pop eax
 
mov esi, 500
call Sleep
 
; must wait till reset is deasserted
mov esi, 100 ; FIXME: 100 tries seem excessive
.while_loop:
test eax, BMCR_RESET
jz .while_loop_exit
 
push esi
mov esi, 10
call Sleep
pop esi
 
mov eax, MII_BMCR
mov ecx, MII_READ
call mii_rw
 
dec esi
jnz .while_loop
 
mov eax, 0xffffffff
jmp .return
 
.while_loop_exit:
xor eax, eax
.return:
pop edx ecx ebx
 
ret
 
 
align 4
pci_push:
 
push eax
mov eax, [edi]
pop eax
 
ret
 
 
 
 
align 4
mac_reset:
 
push esi edi
 
DEBUGF 1,"mac_reset.\n"
 
mov edi, [device.mmio_addr]
mov eax, [device.desc_ver]
or eax, (TXRXCTL_BIT2 or TXRXCTL_RESET)
mov [TxRxControl], eax
call pci_push
 
mov [MacReset], MAC_RESET_ASSERT
call pci_push
 
mov esi, NV_MAC_RESET_DELAY
call Sleep
 
mov [MacReset], 0
call pci_push
 
mov esi, NV_MAC_RESET_DELAY
call Sleep
 
mov eax, [device.desc_ver]
or eax, TXRXCTL_BIT2
mov [TxRxControl], eax
call pci_push
 
pop edi esi
 
ret
 
 
 
 
 
align 4
init_ring:
 
DEBUGF 1,"init rings\n"
push eax esi ecx
 
mov [device.next_tx], 0
 
mov ecx, TX_RING
lea esi, [device.tx_ring]
.tx_loop:
mov [esi + TxDesc.FlagLen], 0
add esi, sizeof.TxDesc
dec ecx
jnz .tx_loop
 
mov [device.cur_rx], 0
 
mov ecx, RX_RING
lea esi, [device.rx_ring]
.rx_loop:
push ecx esi
stdcall KernelAlloc, 4096 shl RBLEN ; push/pop esi not needed, but just in case...
pop esi
mov [esi + RX_RING*sizeof.RxDesc], eax
GetRealAddr
mov [esi + RxDesc.PacketBuffer], eax
mov [esi + RxDesc.FlagLen], (4096 shl RBLEN or NV_RX_AVAIL)
add esi, sizeof.RxDesc
pop ecx
dec ecx
jnz .rx_loop
pop ecx esi eax
 
ret
 
 
 
 
 
; Input: none
; Output: none
align 4
txrx_reset:
 
push eax esi
 
DEBUGF 1,"txrx_reset\n"
 
mov edi, [device.mmio_addr]
mov eax, [device.desc_ver]
or eax, (TXRXCTL_BIT2 or TXRXCTL_RESET)
mov [TxRxControl], eax
call pci_push
 
mov esi, NV_TXRX_RESET_DELAY
call Sleep
 
mov eax, [device.desc_ver]
or eax, TXRXCTL_BIT2
mov [TxRxControl], eax
call pci_push
 
pop esi eax
 
ret
 
 
 
 
 
; Input: none
; Output: none
set_multicast:
 
; u32 addr[2];
; u32 mask[2];
; u32 pff;
; u32 alwaysOff[2];
; u32 alwaysOn[2];
;
; memset(addr, 0, sizeof(addr));
; memset(mask, 0, sizeof(mask));
;
; pff = PFF_MYADDR;
;
; alwaysOn[0] = alwaysOn[1] = alwaysOff[0] = alwaysOff[1] = 0;
;
; addr[0] = alwaysOn[0];
; addr[1] = alwaysOn[1];
; mask[0] = alwaysOn[0] | alwaysOff[0];
; mask[1] = alwaysOn[1] | alwaysOff[1];
;
; addr[0] |= MCASTADDRA_FORCE;
; pff |= PFF_ALWAYS;
 
call stop_rx
 
mov edi, [device.mmio_addr]
mov [MulticastAddrA], MCASTADDRA_FORCE
 
mov [MulticastAddrB], 0
mov [MulticastMaskA], 0
mov [MulticastMaskB], 0
mov [PacketFilterFlags], (PFF_MYADDR or PFF_ALWAYS)
 
call start_rx
 
ret
 
 
 
 
 
; Input: none
; Output: none
start_rx:
 
push edi
 
DEBUGF 1,"start_rx\n"
 
; Already running? Stop it.
mov edi, [device.mmio_addr]
mov eax, [ReceiverControl]
test eax, RCVCTL_START
jz @f
mov [ReceiverControl], 0
call pci_push
@@:
 
mov eax, [device.linkspeed]
mov [LinkSpeed], eax
call pci_push
 
mov [ReceiverControl], RCVCTL_START
call pci_push
 
pop edi
 
ret
 
 
 
 
; Input: none
; Output: none
stop_rx:
 
push esi edi
 
DEBUGF 1,"stop_rx.\n"
 
mov edi, [device.mmio_addr]
mov [ReceiverControl], 0
 
push ebx edx edi
stdcall reg_delay, ReceiverStatus-edi, RCVSTAT_BUSY, 0, NV_RXSTOP_DELAY1, NV_RXSTOP_DELAY1MAX, 0
pop edi edx ebx
 
mov esi, NV_RXSTOP_DELAY2
call Sleep
 
mov [LinkSpeed], 0
 
pop edi esi
 
ret
 
 
 
 
; Input: none
; Output: EAX
update_linkspeed:
 
DEBUGF 1,"update linkspeed\n"
 
; BMSR_LSTATUS is latched, read it twice: we want the current value.
mov edx, [device.phyaddr]
mov eax, MII_BMSR
mov ecx, MII_READ
call mii_rw
 
mov eax, MII_BMSR
mov ecx, MII_READ
call mii_rw
test eax, BMSR_LSTATUS ; Link up?
jz .10mbit_hd
 
test eax, BMSR_ANEGCOMPLETE ; still in autonegotiation?
jz .10mbit_hd
 
cmp [device.gigabit], PHY_GIGABIT
jne .no_gigabit
 
;mov edx, [device.phyaddr]
mov eax, MII_1000BT_CR
mov ecx, MII_READ
call mii_rw
push eax
 
;mov edx, [device.phyaddr]
mov eax, MII_1000BT_SR
mov ecx, MII_READ
call mii_rw
pop ecx
 
test eax, LPA_1000FULL
jz .no_gigabit
test ecx, ADVERTISE_1000FULL
jz .no_gigabit
 
DEBUGF 1,"update_linkspeed: GBit ethernet detected.\n"
mov ecx, (LINKSPEED_FORCE or LINKSPEED_1000)
xor eax, eax
inc eax
jmp set_speed
.no_gigabit:
 
;mov edx, [device.phyaddr]
mov eax, MII_ADVERTISE
mov ecx, MII_READ
call mii_rw ; adv = eax
push eax
 
;mov edx, [device.phyaddr]
mov eax, MII_LPA
mov ecx, MII_READ
call mii_rw ; lpa = eax
pop ecx
 
DEBUGF 1,"PHY advertises 0x%x, lpa 0x%x\n", ecx, eax
and eax, ecx ; FIXME: handle parallel detection properly, handle gigabit ethernet
 
test eax, LPA_100FULL
jz @f
DEBUGF 1,"update_linkspeed: 100 mbit full duplex\n"
mov ecx, (LINKSPEED_FORCE or LINKSPEED_100)
xor eax, eax
inc eax
jmp set_speed
@@:
 
test eax, LPA_100HALF
jz @f
DEBUGF 1,"update_linkspeed: 100 mbit half duplex\n"
mov ecx, (LINKSPEED_FORCE or LINKSPEED_100)
xor eax, eax
jmp set_speed
@@:
 
test eax, LPA_10FULL
jz @f
DEBUGF 1,"update_linkspeed: 10 mbit full duplex\n"
mov ecx, (LINKSPEED_FORCE or LINKSPEED_10)
xor eax, eax
inc eax
jmp set_speed
@@:
 
.10mbit_hd:
DEBUGF 1,"update_linkspeed: 10 mbit half duplex\n"
mov ecx, (LINKSPEED_FORCE or LINKSPEED_10)
xor eax, eax
jmp set_speed
 
 
align 4
set_speed:
 
cmp eax, [device.duplex]
jne .update
cmp ecx, [device.linkspeed]
jne .update
 
ret
 
.update:
DEBUGF 1,"update_linkspeed: changing link to 0x%x/XD.\n", ecx
mov [device.duplex], eax
mov [device.linkspeed], ecx
cmp [device.gigabit], PHY_GIGABIT
jne .no_gigabit
 
mov edi, [device.mmio_addr]
mov eax, [RandomSeed]
 
and eax, not (0x3FF00)
mov ecx, eax ; phyreg = ecx
 
mov eax, [device.linkspeed]
and eax, 0xFFF
cmp eax, LINKSPEED_10
jne @f
or ecx, RNDSEED_FORCE3
jmp .end_if4
@@:
 
cmp eax, LINKSPEED_100
jne @f
or ecx, RNDSEED_FORCE2
jmp .end_if4
@@:
 
cmp eax, LINKSPEED_1000
jne .end_if4
or ecx, RNDSEED_FORCE
.end_if4:
mov [RandomSeed], ecx
.no_gigabit:
 
mov ecx, [PhyInterface]
and ecx, not (PHY_HALF or PHY_100 or PHY_1000)
 
cmp [device.duplex], 0
jne @f
or ecx, PHY_HALF
@@:
 
mov eax, [device.linkspeed]
and eax, 0xFFF
cmp eax, LINKSPEED_100
jne @f
or ecx, PHY_100
jmp .end_if5
@@:
 
cmp eax, LINKSPEED_1000
jne .end_if5
or ecx, PHY_1000
 
.end_if5:
mov [PhyInterface], ecx
cmp [device.duplex], 0
je @f
xor ecx, ecx
jmp .next
@@:
 
mov ecx, MISC1_HD
.next:
or ecx, MISC1_FORCE
mov [Misc1], ecx
 
call pci_push
 
mov eax, [device.linkspeed]
mov [LinkSpeed], eax
 
call pci_push
 
ret
 
 
 
 
 
 
align 4
read_mac:
 
mov edi, [device.mmio_addr]
 
mov eax, [MacAddrA]
mov ecx, [MacAddrB]
 
mov dword [device.mac], eax
mov word [device.mac + 4], cx
 
cmp [device.device_id], 0x03E5
jae @f
bswap eax
xchg cl, ch
mov dword [device.mac + 2], eax
mov word [device.mac], cx
@@:
 
DEBUGF 1,"MAC = %x-%x-%x-%x-%x-%x\n", \
[device.mac+0]:2,[device.mac+1]:2,[device.mac+2]:2,[device.mac+3]:2,[device.mac+4]:2,[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
transmit:
DEBUGF 2,"\nTransmitting packet, buffer:%x, size:%u\n", [esp+4], [esp+8]
mov eax, [esp+4]
DEBUGF 2,"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 dword [esp + 8], 1514
ja .fail
cmp dword [esp + 8], 60
jb .fail
 
; get the descriptor address
mov eax, [device.next_tx]
mov cl, sizeof.TxDesc
mul cl
lea esi, [device.tx_ring + eax]
mov eax, [esp + 4]
mov [esi + TX_RING*sizeof.TxDesc], eax
GetRealAddr
mov [esi + TxDesc.PacketBuffer], eax
 
mov ecx, [esp + 8]
or ecx, [device.txflags]
mov [esi + TxDesc.FlagLen], eax
 
mov edi, [device.mmio_addr]
mov eax, [device.desc_ver]
or eax, TXRXCTL_KICK
mov [TxRxControl], eax
 
call pci_push
 
inc [device.next_tx]
and [device.next_tx], (TX_RING-1)
 
; Update stats
inc [device.packets_tx]
mov eax, [esp + 8]
add dword [device.bytes_tx], eax
adc dword [device.bytes_tx + 4], 0
 
xor eax, eax
ret 8
 
.fail:
xor eax, eax
inc eax
ret 8
 
 
 
 
 
 
; Interrupt handler
align 4
int_handler:
 
push ebx esi edi
 
DEBUGF 2,"\n%s INT\n", my_service
 
;-------------------------------------------
; Find pointer of device wich made IRQ occur
 
mov esi, device_list
mov ecx, [devices]
test ecx, ecx
jz .fail
.nextdevice:
mov ebx, dword [esi]
add esi, 4
 
mov edi, [device.mmio_addr]
mov eax, [IrqStatus]
test eax, eax
jnz .got_it
dec ecx
jnz .nextdevice
.nothing:
pop edi esi ebx
xor eax, eax
 
ret
 
.got_it:
mov [IrqStatus], eax
DEBUGF 2,"IrqStatus = %x\n", eax
 
test eax, IRQ_RX
jz .no_rx
 
.top:
mov eax, [device.cur_rx]
mov cx, sizeof.RxDesc
mul cx
lea esi, [device.rx_ring + eax]
mov eax, [esi + RxDesc.FlagLen]
 
test eax, NV_RX_AVAIL ; still owned by hardware
jnz .return0
 
cmp [device.desc_ver], DESC_VER_1
jne @f
test eax, NV_RX_DESCRIPTORVALID
jz .return0
jmp .next
@@:
test eax, NV_RX2_DESCRIPTORVALID
jz .return0
 
.next:
 
cmp dword [device.desc_ver], DESC_VER_1
jne @f
and eax, LEN_MASK_V1
jmp .next2
@@:
and eax, LEN_MASK_V2
.next2:
 
; got a valid packet - forward it to the network core
push .top
push eax
push dword [esi + RX_RING*sizeof.RxDesc]
 
inc [device.cur_rx]
and [device.cur_rx], (RX_RING-1)
 
; Allocate new buffer
 
stdcall KernelAlloc, 4096 shl RBLEN
mov [esi + RX_RING*sizeof.RxDesc], eax
GetRealAddr
mov [esi + RxDesc.PacketBuffer], eax
mov [esi + RxDesc.FlagLen], (4096 shl RBLEN or NV_RX_AVAIL)
 
jmp Eth_input
 
.return0:
 
 
.no_rx:
test eax, IRQ_RX_ERROR
jz .no_rx_err
 
push eax
DEBUGF 2,"RX error!\n"
 
mov eax, [device.cur_rx]
mov cx, sizeof.RxDesc
mul cx
lea esi, [device.rx_ring + eax]
mov eax, [esi + RxDesc.FlagLen]
 
DEBUGF 2,"Flaglen=%x\n", eax
 
; TODO: allocate new buff
pop eax
 
.no_rx_err:
test eax, IRQ_LINK
jz .no_link
 
push eax
call update_linkspeed
pop eax
 
.no_link:
.fail:
 
pop edi esi ebx
xor eax, eax
inc eax
 
ret
 
 
 
 
; End of code
 
section '.data' data readable writable align 16 ; place all uninitialized data place here
align 4 ; Place all initialised data here
 
devices dd 0
version dd (DRIVER_VERSION shl 16) or (API_VERSION and 0xFFFF)
my_service db 'FORCEDETH',0 ; max 16 chars include zero
 
include_debug_strings ; All data wich FDO uses will be included here
 
device_list rd MAX_DEVICES ; This list contains all pointers to device structures the driver is handling
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
/drivers/ethernet/i8254x.asm
0,0 → 1,818
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; i8254x driver for KolibriOS ;;
;; ;;
;; based on i8254x.asm from baremetal os ;;
;; ;;
;; Written by hidnplayr (hidnplayr@gmail.com) ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
; TODO: make better use of the available descriptors
 
format MS COFF
 
API_VERSION = 0x01000100
DRIVER_VERSION = 5
 
MAX_DEVICES = 16
 
DEBUG = 1
__DEBUG__ = 1
__DEBUG_LEVEL__ = 2
 
MAX_PKT_SIZE = 16384 ; Maximum packet size
 
 
include '../proc32.inc'
include '../imports.inc'
include '../fdo.inc'
include '../struct.inc'
include '../netdrv.inc'
 
public START
public service_proc
public version
 
 
 
; Register list
REG_CTRL = 0x0000 ; Control Register
REG_STATUS = 0x0008 ; Device Status Register
REG_CTRLEXT = 0x0018 ; Extended Control Register
REG_MDIC = 0x0020 ; MDI Control Register
REG_FCAL = 0x0028 ; Flow Control Address Low
REG_FCAH = 0x002C ; Flow Control Address High
REG_FCT = 0x0030 ; Flow Control Type
REG_VET = 0x0038 ; VLAN Ether Type
REG_ICR = 0x00C0 ; Interrupt Cause Read
REG_ITR = 0x00C4 ; Interrupt Throttling Register
REG_ICS = 0x00C8 ; Interrupt Cause Set Register
REG_IMS = 0x00D0 ; Interrupt Mask Set/Read Register
REG_IMC = 0x00D8 ; Interrupt Mask Clear Register
REG_RCTL = 0x0100 ; Receive Control Register
REG_FCTTV = 0x0170 ; Flow Control Transmit Timer Value
REG_TXCW = 0x0178 ; Transmit Configuration Word
REG_RXCW = 0x0180 ; Receive Configuration Word
REG_TCTL = 0x0400 ; Transmit Control Register
REG_TIPG = 0x0410 ; Transmit Inter Packet Gap
 
REG_LEDCTL = 0x0E00 ; LED Control
REG_PBA = 0x1000 ; Packet Buffer Allocation
 
REG_RDBAL = 0x2800 ; RX Descriptor Base Address Low
REG_RDBAH = 0x2804 ; RX Descriptor Base Address High
REG_RDLEN = 0x2808 ; RX Descriptor Length
REG_RDH = 0x2810 ; RX Descriptor Head
REG_RDT = 0x2818 ; RX Descriptor Tail
REG_RDTR = 0x2820 ; RX Delay Timer Register
REG_RXDCTL = 0x3828 ; RX Descriptor Control
REG_RADV = 0x282C ; RX Int. Absolute Delay Timer
REG_RSRPD = 0x2C00 ; RX Small Packet Detect Interrupt
 
REG_TXDMAC = 0x3000 ; TX DMA Control
REG_TDBAL = 0x3800 ; TX Descriptor Base Address Low
REG_TDBAH = 0x3804 ; TX Descriptor Base Address High
REG_TDLEN = 0x3808 ; TX Descriptor Length
REG_TDH = 0x3810 ; TX Descriptor Head
REG_TDT = 0x3818 ; TX Descriptor Tail
REG_TIDV = 0x3820 ; TX Interrupt Delay Value
REG_TXDCTL = 0x3828 ; TX Descriptor Control
REG_TADV = 0x382C ; TX Absolute Interrupt Delay Value
REG_TSPMT = 0x3830 ; TCP Segmentation Pad & Min Threshold
 
REG_RXCSUM = 0x5000 ; RX Checksum Control
 
; Register list for i8254x
I82542_REG_RDTR = 0x0108 ; RX Delay Timer Register
I82542_REG_RDBAL = 0x0110 ; RX Descriptor Base Address Low
I82542_REG_RDBAH = 0x0114 ; RX Descriptor Base Address High
I82542_REG_RDLEN = 0x0118 ; RX Descriptor Length
I82542_REG_RDH = 0x0120 ; RDH for i82542
I82542_REG_RDT = 0x0128 ; RDT for i82542
I82542_REG_TDBAL = 0x0420 ; TX Descriptor Base Address Low
I82542_REG_TDBAH = 0x0424 ; TX Descriptor Base Address Low
I82542_REG_TDLEN = 0x0428 ; TX Descriptor Length
I82542_REG_TDH = 0x0430 ; TDH for i82542
I82542_REG_TDT = 0x0438 ; TDT for i82542
 
; CTRL - Control Register (0x0000)
CTRL_FD = 0x00000001 ; Full Duplex
CTRL_LRST = 0x00000008 ; Link Reset
CTRL_ASDE = 0x00000020 ; Auto-speed detection
CTRL_SLU = 0x00000040 ; Set Link Up
CTRL_ILOS = 0x00000080 ; Invert Loss of Signal
CTRL_SPEED_MASK = 0x00000300 ; Speed selection
CTRL_SPEED_SHIFT = 8
CTRL_FRCSPD = 0x00000800 ; Force Speed
CTRL_FRCDPLX = 0x00001000 ; Force Duplex
CTRL_SDP0_DATA = 0x00040000 ; SDP0 data
CTRL_SDP1_DATA = 0x00080000 ; SDP1 data
CTRL_SDP0_IODIR = 0x00400000 ; SDP0 direction
CTRL_SDP1_IODIR = 0x00800000 ; SDP1 direction
CTRL_RST = 0x04000000 ; Device Reset
CTRL_RFCE = 0x08000000 ; RX Flow Ctrl Enable
CTRL_TFCE = 0x10000000 ; TX Flow Ctrl Enable
CTRL_VME = 0x40000000 ; VLAN Mode Enable
CTRL_PHY_RST = 0x80000000 ; PHY reset
 
; STATUS - Device Status Register (0x0008)
STATUS_FD = 0x00000001 ; Full Duplex
STATUS_LU = 0x00000002 ; Link Up
STATUS_TXOFF = 0x00000010 ; Transmit paused
STATUS_TBIMODE = 0x00000020 ; TBI Mode
STATUS_SPEED_MASK = 0x000000C0 ; Link Speed setting
STATUS_SPEED_SHIFT = 6
STATUS_ASDV_MASK = 0x00000300 ; Auto Speed Detection
STATUS_ASDV_SHIFT = 8
STATUS_PCI66 = 0x00000800 ; PCI bus speed
STATUS_BUS64 = 0x00001000 ; PCI bus width
STATUS_PCIX_MODE = 0x00002000 ; PCI-X mode
STATUS_PCIXSPD_MASK = 0x0000C000 ; PCI-X speed
STATUS_PCIXSPD_SHIFT = 14
 
; CTRL_EXT - Extended Device Control Register (0x0018)
CTRLEXT_PHY_INT = 0x00000020 ; PHY interrupt
CTRLEXT_SDP6_DATA = 0x00000040 ; SDP6 data
CTRLEXT_SDP7_DATA = 0x00000080 ; SDP7 data
CTRLEXT_SDP6_IODIR = 0x00000400 ; SDP6 direction
CTRLEXT_SDP7_IODIR = 0x00000800 ; SDP7 direction
CTRLEXT_ASDCHK = 0x00001000 ; Auto-Speed Detect Chk
CTRLEXT_EE_RST = 0x00002000 ; EEPROM reset
CTRLEXT_SPD_BYPS = 0x00008000 ; Speed Select Bypass
CTRLEXT_RO_DIS = 0x00020000 ; Relaxed Ordering Dis.
CTRLEXT_LNKMOD_MASK = 0x00C00000 ; Link Mode
CTRLEXT_LNKMOD_SHIFT = 22
 
; MDIC - MDI Control Register (0x0020)
MDIC_DATA_MASK = 0x0000FFFF ; Data
MDIC_REG_MASK = 0x001F0000 ; PHY Register
MDIC_REG_SHIFT = 16
MDIC_PHY_MASK = 0x03E00000 ; PHY Address
MDIC_PHY_SHIFT = 21
MDIC_OP_MASK = 0x0C000000 ; Opcode
MDIC_OP_SHIFT = 26
MDIC_R = 0x10000000 ; Ready
MDIC_I = 0x20000000 ; Interrupt Enable
MDIC_E = 0x40000000 ; Error
 
; ICR - Interrupt Cause Read (0x00c0)
ICR_TXDW = 0x00000001 ; TX Desc Written back
ICR_TXQE = 0x00000002 ; TX Queue Empty
ICR_LSC = 0x00000004 ; Link Status Change
ICR_RXSEQ = 0x00000008 ; RX Sence Error
ICR_RXDMT0 = 0x00000010 ; RX Desc min threshold reached
ICR_RXO = 0x00000040 ; RX Overrun
ICR_RXT0 = 0x00000080 ; RX Timer Interrupt
ICR_MDAC = 0x00000200 ; MDIO Access Complete
ICR_RXCFG = 0x00000400
ICR_PHY_INT = 0x00001000 ; PHY Interrupt
ICR_GPI_SDP6 = 0x00002000 ; GPI on SDP6
ICR_GPI_SDP7 = 0x00004000 ; GPI on SDP7
ICR_TXD_LOW = 0x00008000 ; TX Desc low threshold hit
ICR_SRPD = 0x00010000 ; Small RX packet detected
 
; RCTL - Receive Control Register (0x0100)
RCTL_EN = 0x00000002 ; Receiver Enable
RCTL_SBP = 0x00000004 ; Store Bad Packets
RCTL_UPE = 0x00000008 ; Unicast Promiscuous Enabled
RCTL_MPE = 0x00000010 ; Xcast Promiscuous Enabled
RCTL_LPE = 0x00000020 ; Long Packet Reception Enable
RCTL_LBM_MASK = 0x000000C0 ; Loopback Mode
RCTL_LBM_SHIFT = 6
RCTL_RDMTS_MASK = 0x00000300 ; RX Desc Min Threshold Size
RCTL_RDMTS_SHIFT = 8
RCTL_MO_MASK = 0x00003000 ; Multicast Offset
RCTL_MO_SHIFT = 12
RCTL_BAM = 0x00008000 ; Broadcast Accept Mode
RCTL_BSIZE_MASK = 0x00030000 ; RX Buffer Size
RCTL_BSIZE_SHIFT = 16
RCTL_VFE = 0x00040000 ; VLAN Filter Enable
RCTL_CFIEN = 0x00080000 ; CFI Enable
RCTL_CFI = 0x00100000 ; Canonical Form Indicator Bit
RCTL_DPF = 0x00400000 ; Discard Pause Frames
RCTL_PMCF = 0x00800000 ; Pass MAC Control Frames
RCTL_BSEX = 0x02000000 ; Buffer Size Extension
RCTL_SECRC = 0x04000000 ; Strip Ethernet CRC
 
; TCTL - Transmit Control Register (0x0400)
TCTL_EN = 0x00000002 ; Transmit Enable
TCTL_PSP = 0x00000008 ; Pad short packets
TCTL_SWXOFF = 0x00400000 ; Software XOFF Transmission
 
; PBA - Packet Buffer Allocation (0x1000)
PBA_RXA_MASK = 0x0000FFFF ; RX Packet Buffer
PBA_RXA_SHIFT = 0
PBA_TXA_MASK = 0xFFFF0000 ; TX Packet Buffer
PBA_TXA_SHIFT = 16
 
; Flow Control Type
FCT_TYPE_DEFAULT = 0x8808
 
; === TX Descriptor fields ===
 
; TX Packet Length (word 2)
TXDESC_LEN_MASK = 0x0000ffff
 
; TX Descriptor CMD field (word 2)
TXDESC_IDE = 0x80000000 ; Interrupt Delay Enable
TXDESC_VLE = 0x40000000 ; VLAN Packet Enable
TXDESC_DEXT = 0x20000000 ; Extension
TXDESC_RPS = 0x10000000 ; Report Packet Sent
TXDESC_RS = 0x08000000 ; Report Status
TXDESC_IC = 0x04000000 ; Insert Checksum
TXDESC_IFCS = 0x02000000 ; Insert FCS
TXDESC_EOP = 0x01000000 ; End Of Packet
 
; TX Descriptor STA field (word 3)
TXDESC_TU = 0x00000008 ; Transmit Underrun
TXDESC_LC = 0x00000004 ; Late Collision
TXDESC_EC = 0x00000002 ; Excess Collisions
TXDESC_DD = 0x00000001 ; Descriptor Done
 
; === RX Descriptor fields ===
 
; RX Packet Length (word 2)
RXDESC_LEN_MASK = 0x0000ffff
 
; RX Descriptor STA field (word 3)
RXDESC_PIF = 0x00000080 ; Passed In-exact Filter
RXDESC_IPCS = 0x00000040 ; IP cksum calculated
RXDESC_TCPCS = 0x00000020 ; TCP cksum calculated
RXDESC_VP = 0x00000008 ; Packet is 802.1Q
RXDESC_IXSM = 0x00000004 ; Ignore cksum indication
RXDESC_EOP = 0x00000002 ; End Of Packet
RXDESC_DD = 0x00000001 ; Descriptor Done
 
 
virtual at ebx
device:
ETH_DEVICE
 
.mmio_addr dd ?
.pci_bus dd ?
.pci_dev dd ?
.irq_line db ?
 
.cur_tx dd ?
.last_tx dd ?
 
rb 0x100 - (($ - device) and 0xff)
.rx_desc rd 256/8
 
rb 0x100 - (($ - device) and 0xff)
.tx_desc rd 256/8
 
sizeof.device_struct = $ - device
 
end virtual
 
section '.flat' code readable align 16
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; proc START ;;
;; ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
proc START stdcall, state:dword
 
cmp [state], 1
jne .exit
 
.entry:
 
DEBUGF 2,"Loading %s driver\n", my_service
stdcall RegService, my_service, service_proc
ret
 
.fail:
.exit:
xor eax, eax
ret
 
endp
 
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; proc SERVICE_PROC ;;
;; ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
proc service_proc stdcall, ioctl:dword
 
mov edx, [ioctl]
mov eax, [IOCTL.io_code]
 
;------------------------------------------------------
 
cmp eax, 0 ;SRV_GETVERSION
jne @F
 
cmp [IOCTL.out_size], 4
jb .fail
mov eax, [IOCTL.output]
mov [eax], dword API_VERSION
 
xor eax, eax
ret
 
;------------------------------------------------------
@@:
cmp eax, 1 ;SRV_HOOK
jne .fail
 
cmp [IOCTL.inp_size], 3 ; Data input must be at least 3 bytes
jb .fail
 
mov eax, [IOCTL.input]
cmp byte [eax], 1 ; 1 means device number and bus number (pci) are given
jne .fail ; other types arent supported for this card yet
 
; check if the device is already listed
 
mov esi, device_list
mov ecx, [devices]
test ecx, ecx
jz .firstdevice
 
; mov eax, [IOCTL.input] ; get the pci bus and device numbers
mov ax, [eax+1] ;
.nextdevice:
mov ebx, [esi]
cmp al, byte [device.pci_bus]
jne .next
cmp ah, byte [device.pci_dev]
je .find_devicenum ; Device is already loaded, let's find it's device number
.next:
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_struct, .fail ; Allocate the buffer for device structure
 
; Fill in the direct call addresses into the struct
 
mov [device.reset], reset
mov [device.transmit], transmit
mov [device.unload], unload
mov [device.name], my_service
 
; save the pci bus and device numbers
 
mov eax, [IOCTL.input]
movzx ecx, byte [eax+1]
mov [device.pci_bus], ecx
movzx ecx, byte [eax+2]
mov [device.pci_dev], ecx
 
; Now, it's time to find the base mmio addres of the PCI device
 
PCI_find_mmio32
 
; Create virtual mapping of the physical memory
 
push 1Bh ; PG_SW+PG_NOCACHE
push 10000h ; size of the map
push eax
call MapIoMem
mov [device.mmio_addr], eax
 
; We've found the mmio address, find IRQ now
 
PCI_find_irq
 
DEBUGF 1,"Hooking into device, dev:%x, bus:%x, irq:%x, addr:%x\n",\
[device.pci_dev]:1,[device.pci_bus]:1,[device.irq_line]:1,[device.mmio_addr]:8
 
; Ok, the eth_device structure is ready, let's probe the device
call probe ; this function will output in eax
test eax, eax
jnz .err ; If an error occured, exit
 
mov eax, [devices] ; Add the device structure to our device list
mov [device_list+4*eax], ebx ; (IRQ handler uses this list to find device)
inc [devices] ;
 
call start_i8254x
 
mov [device.type], NET_TYPE_ETH
call 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"
call NetPtrToNum ; This kernel procedure converts a pointer to device struct in ebx
; into a device number in edi
mov eax, edi ; Application wants it in eax instead
DEBUGF 1,"Kernel says: %u\n", eax
ret
 
; If an error occured, remove all allocated data and exit (returning -1 in eax)
 
.destroy:
; todo: reset device into virgin state
 
.err:
stdcall KernelFree, ebx
 
.fail:
or eax, -1
ret
 
;------------------------------------------------------
endp
 
 
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
;; ;;
;; Actual Hardware dependent code starts here ;;
;; ;;
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
 
 
align 4
unload:
; TODO: (in this particular order)
;
; - Stop the device
; - Detach int handler
; - Remove device from local list (device_list)
; - call unregister function in kernel
; - Remove all allocated structures and buffers the card used
 
or eax, -1
 
ret
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; probe: enables the device (if it really is I8254X)
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
align 4
probe:
 
DEBUGF 1,"Probe\n"
 
PCI_make_bus_master
 
; TODO: validate the device
 
call read_mac
 
movzx eax, [device.irq_line]
DEBUGF 1,"Attaching int handler to irq %x\n", eax:1
stdcall AttachIntHandler, eax, int_handler, dword 0
test eax, eax
jnz @f
DEBUGF 1,"\nCould not attach int handler!\n"
; or eax, -1
; ret
@@:
 
 
reset_dontstart:
DEBUGF 1,"Reset\n"
 
mov esi, [device.mmio_addr]
 
or dword [esi + REG_CTRL], CTRL_RST ; reset device
.loop:
push esi
xor esi, esi
inc esi
call Sleep
pop esi
test dword [esi + REG_CTRL], CTRL_RST
jnz .loop
 
mov dword [esi + REG_IMC], 0xffffffff ; Disable all interrupt causes
mov eax, dword [esi + REG_ICR] ; Clear any pending interrupts
mov dword [esi + REG_ITR], 0 ; Disable interrupt throttling logic
 
mov dword [esi + REG_PBA], 0x00000030 ; PBA: set the RX buffer size to 48KB (TX buffer is calculated as 64-RX buffer)
mov dword [esi + REG_RDTR], 0 ; RDTR: set no delay
 
mov dword [esi + REG_TXCW], 0x08008060 ; TXCW: set ANE, TxConfigWord (Half/Full duplex, Next Page Reqest)
 
mov eax, [esi + REG_CTRL]
or eax, 1 shl 6 + 1 shl 5
and eax, not (1 shl 3 + 1 shl 7 + 1 shl 30 + 1 shl 31)
mov dword [esi + REG_CTRL], eax ; CTRL: clear LRST, set SLU and ASDE, clear RSTPHY, VME, and ILOS
 
lea edi, [esi + 0x5200] ; MTA: reset
mov eax, 0xffffffff
stosd
stosd
stosd
stosd
 
stdcall KernelAlloc, 48*1024
mov dword [device.rx_desc + 16], eax
GetRealAddr
mov dword [device.rx_desc], eax
mov dword [device.rx_desc + 4], 0
 
lea eax, [device.rx_desc]
GetRealAddr
mov dword [esi + REG_RDBAL], eax ; Receive Descriptor Base Address Low
mov dword [esi + REG_RDBAH], 0 ; Receive Descriptor Base Address High
mov dword [esi + REG_RDLEN], (1 * 128) ; Receive Descriptor Length
mov dword [esi + REG_RDH], 0 ; Receive Descriptor Head
mov dword [esi + REG_RDT], 1 ; Receive Descriptor Tail
mov dword [esi + REG_RCTL], RCTL_EN or RCTL_SBP or RCTL_BAM or RCTL_SECRC or RCTL_UPE or RCTL_MPE
; Receiver Enable, Store Bad Packets, Broadcast Accept Mode, Strip Ethernet CRC from incoming packet, Promiscuous mode
 
mov dword [device.tx_desc], 0
mov dword [device.tx_desc + 4], 0
mov dword [device.tx_desc + 16], 0
 
lea eax, [device.tx_desc]
GetRealAddr
mov dword [esi + REG_TDBAL], eax ; Transmit Descriptor Base Address Low
mov dword [esi + REG_TDBAH], 0 ; Transmit Descriptor Base Address High
mov dword [esi + REG_TDLEN], (1 * 128) ; Transmit Descriptor Length
mov dword [esi + REG_TDH], 0 ; Transmit Descriptor Head
mov dword [esi + REG_TDT], 0 ; Transmit Descriptor Tail
mov dword [esi + REG_TCTL], 0x010400fa ; Enabled, Pad Short Packets, 15 retrys, 64-byte COLD, Re-transmit on Late Collision
mov dword [esi + REG_TIPG], 0x0060200A ; IPGT 10, IPGR1 8, IPGR2 6
 
xor eax, eax
ret
 
align 4
reset:
call reset_dontstart
 
start_i8254x:
 
xor eax, eax
mov [esi + REG_RDTR], eax ; Clear the Receive Delay Timer Register
mov [esi + REG_RADV], eax ; Clear the Receive Interrupt Absolute Delay Timer
mov [esi + REG_RSRPD], eax ; Clear the Receive Small Packet Detect Interrupt
; or eax, 1 shl 0 + 1 shl 7 ; TXDW + RXT0
mov eax, 1+4+16 ;;;; hack!
mov [esi + REG_IMS], eax ; Enable interrupt types
 
mov [device.mtu], 1514
 
; Set link state to unknown
mov [device.state], ETH_LINK_UNKOWN
 
xor eax, eax
ret
 
 
 
 
align 4
read_mac:
 
DEBUGF 1,"Read MAC\n"
 
mov esi, [device.mmio_addr]
 
mov eax, [esi+0x5400] ; RAL
test eax, eax
jz .try_eeprom
 
mov dword [device.mac], eax
mov eax, [esi+0x5404] ; RAH
mov word [device.mac+4], ax
 
jmp .mac_ok
 
.try_eeprom:
mov dword [esi+0x14], 0x00000001
mov eax, [esi+0x14]
shr eax, 16
mov word [device.mac], ax
 
mov dword [esi+0x14], 0x00000101
mov eax, [esi+0x14]
shr eax, 16
mov word [device.mac+2], ax
 
mov dword [esi+0x14], 0x00000201
mov eax, [esi+0x14]
shr eax, 16
mov word [device.mac+4], ax
 
.mac_ok:
DEBUGF 1,"MAC = %x-%x-%x-%x-%x-%x\n",\
[device.mac+0]:2,[device.mac+1]:2,[device.mac+2]:2,[device.mac+3]:2,[device.mac+4]:2,[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
transmit:
DEBUGF 2,"\nTransmitting packet, buffer:%x, size:%u\n", [esp+4], [esp+8]
mov eax, [esp+4]
DEBUGF 2,"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 dword [esp + 8], 1514
ja .fail
cmp dword [esp + 8], 60
jb .fail
 
 
; Program the descriptor (use legacy mode)
lea edi, [device.tx_desc] ; Transmit Descriptor Base Address
mov dword [edi + 16], eax ; Store the data location (for driver)
GetRealAddr ;
mov dword [edi], eax ; Real addr (for i8254x)
mov dword [edi + 4], 0x00000000 ;
 
mov ecx, [esp + 8]
or ecx, 1 shl 24 + 1 shl 25 + 1 shl 27 ; EOP + IFCS + RS
mov dword [edi + 8], ecx ; Packet size
mov dword [edi + 12], 0x00000000
 
; Tell i8254x wich descriptor(s) we programmed
mov edi, [device.mmio_addr]
mov dword [edi + REG_TDH], 0 ; TDH - Transmit Descriptor Head
mov dword [edi + REG_TDT], 1 ; TDT - Transmit Descriptor Tail
 
; Update stats
inc [device.packets_tx]
mov eax, [esp + 8]
add dword [device.bytes_tx], eax
adc dword [device.bytes_tx + 4], 0
 
ret 8
 
.fail:
DEBUGF 1,"Send failed\n"
ret 8
 
 
;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Interrupt handler ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
int_handler:
 
push ebx esi edi
 
DEBUGF 1,"\n%s int\n", my_service
;-------------------------------------------
; 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 edi, [device.mmio_addr]
mov eax, [edi + REG_ICR]
test eax, eax
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 ", ebx, eax
 
;---------
; RX done?
 
test eax, ICR_RXDMT0
jz .no_rx
 
push eax ebx
push .retaddr
 
; Get last descriptor addr
lea esi, [device.rx_desc]
 
cmp byte [esi + 12], 0 ; Check status field
je .retaddr
 
movzx ecx, word [esi + 8] ; Get the packet length
DEBUGF 2,"got %u bytes\n", ecx
push ecx
push dword [esi + 16] ; Get packet pointer
 
; Update stats
add dword [device.bytes_rx], ecx
adc dword [device.bytes_rx + 4], 0
inc dword [device.packets_rx]
 
; allocate new descriptor
stdcall KernelAlloc, 48*1024
mov dword [esi + 16], eax
GetRealAddr
mov dword [esi], eax
 
; reset descriptor status
mov esi, [device.mmio_addr]
mov dword [esi + REG_RDH], 0x00000000 ; Receive Descriptor Head
mov dword [esi + REG_RDT], 0x00000001 ; Receive Descriptor Tail
 
jmp Eth_input
.retaddr:
pop ebx eax
 
.no_rx:
 
;--------------
; Link Changed?
 
test eax, ICR_LSC
jz .no_link
 
DEBUGF 2,"Link Changed\n"
 
.no_link:
 
;---------------
; Transmit done?
 
test eax, ICR_TXDW
jz .no_tx
 
DEBUGF 2,"Transmit done\n"
 
lea edi, [device.tx_desc] ; Transmit Descriptor Base Address
push dword [edi + 16] ; Store the data location (for driver)
call KernelFree
 
.no_tx:
.fail:
pop edi esi ebx
xor eax, eax
inc eax
 
ret
 
 
 
 
; End of code
 
section '.data' data readable writable align 16
align 4
 
devices dd 0
version dd (DRIVER_VERSION shl 16) or (API_VERSION and 0xFFFF)
my_service db 'I8254X',0 ; max 16 chars include zero
 
include_debug_strings ; All data wich FDO uses will be included here
 
device_list rd MAX_DEVICES ; This list contains all pointers to device structures the driver is handling
 
 
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
/drivers/ethernet/i8255x.asm
0,0 → 1,1255
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; i8255x (Intel eepro 100) driver for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;; Some parts of this driver are based on the code of eepro100.c ;;
;; from linux. ;;
;; ;;
;; Intel's programming manual for i8255x: ;;
;; http://www.intel.com/design/network/manuals/8255x_opensdm.htm ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
; TODO: use separate descriptors in memory instead of placing them in front of packets!
 
 
format MS COFF
 
API_VERSION = 0x01000100
DRIVER_VERSION = 5
 
MAX_DEVICES = 16
 
DEBUG = 1
__DEBUG__ = 1
__DEBUG_LEVEL__ = 2
 
include '../proc32.inc'
include '../imports.inc'
include '../fdo.inc'
include '../netdrv.inc'
 
public START
public service_proc
public version
 
virtual at ebx
 
device:
 
ETH_DEVICE
 
.io_addr dd ?
.pci_bus dd ?
.pci_dev dd ?
.irq_line db ?
 
.rx_desc dd ?
 
.ee_bus_width db ?
 
rb 0x100 - (($ - device) and 0xff)
 
txfd:
.status dw ?
.command dw ?
.link dd ?
.tx_desc_addr dd ?
.count dd ?
 
.tx_buf_addr0 dd ?
.tx_buf_size0 dd ?
 
rb 0x100 - (($ - device) and 0xff)
 
confcmd:
.status dw ?
.command dw ?
.link dd ?
.data rb 64
 
rb 0x100 - (($ - device) and 0xff)
 
lstats:
tx_good_frames dd ?
tx_coll16_errs dd ?
tx_late_colls dd ?
tx_underruns dd ?
tx_lost_carrier dd ?
tx_deferred dd ?
tx_one_colls dd ?
tx_multi_colls dd ?
tx_total_colls dd ?
 
rx_good_frames dd ?
rx_crc_errs dd ?
rx_align_errs dd ?
rx_resource_errs dd ?
rx_overrun_errs dd ?
rx_colls_errs dd ?
rx_runt_errs dd ?
 
last_tx_buffer dd ? ;;; fixme
 
sizeof.device_struct = $ - device
 
end virtual
 
 
virtual at 0
 
rxfd:
.status dw ?
.command dw ?
.link dd ?
.rx_buf_addr dd ?
.count dw ?
.size dw ?
.packet:
 
end virtual
 
 
; Serial EEPROM
 
EE_SK = 1 shl 0 ; serial clock
EE_CS = 1 shl 1 ; chip select
EE_DI = 1 shl 2 ; data in
EE_DO = 1 shl 3 ; data out
EE_MASK = EE_SK + EE_CS + EE_DI + EE_DO
 
; opcodes, first bit is start bit and must be 1
EE_READ = 110b
EE_WRITE = 101b
EE_ERASE = 111b
 
; The SCB accepts the following controls for the Tx and Rx units:
 
CU_START = 0x0010
CU_RESUME = 0x0020
CU_STATSADDR = 0x0040
CU_SHOWSTATS = 0x0050 ; Dump statistics counters.
CU_CMD_BASE = 0x0060 ; Base address to add CU commands.
CU_DUMPSTATS = 0x0070 ; Dump then reset stats counters.
 
RX_START = 0x0001
RX_RESUME = 0x0002
RX_ABORT = 0x0004
RX_ADDR_LOAD = 0x0006
RX_RESUMENR = 0x0007
INT_MASK = 0x0100
DRVR_INT = 0x0200 ; Driver generated interrupt
 
CmdIASetup = 0x0001
CmdConfigure = 0x0002
CmdTx = 0x0004
CmdTxFlex = 0x0008
Cmdsuspend = 0x4000
 
 
reg_scb_status = 0
reg_scb_cmd = 2
reg_scb_ptr = 4
reg_port = 8
reg_eeprom = 14
reg_mdi_ctrl = 16
 
 
macro delay {
push eax
in ax, dx
in ax, dx
in ax, dx
in ax, dx
in ax, dx
in ax, dx
in ax, dx
in ax, dx
in ax, dx
in ax, dx
pop eax
}
 
section '.flat' code readable align 16
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; proc START ;;
;; ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
proc START stdcall, state:dword
 
cmp [state], 1
jne .exit
 
.entry:
 
DEBUGF 1,"Loading %s driver\n", my_service
stdcall RegService, my_service, service_proc
ret
 
.fail:
.exit:
xor eax, eax
ret
 
endp
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; proc SERVICE_PROC ;;
;; ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
proc service_proc stdcall, ioctl:dword
 
mov edx, [ioctl]
mov eax, [IOCTL.io_code]
 
;------------------------------------------------------
 
cmp eax, 0 ;SRV_GETVERSION
jne @F
 
cmp [IOCTL.out_size], 4
jb .fail
mov eax, [IOCTL.output]
mov [eax], dword API_VERSION
 
xor eax, eax
ret
 
;------------------------------------------------------
@@:
cmp eax, 1 ;SRV_HOOK
jne .fail
 
cmp [IOCTL.inp_size], 3 ; Data input must be at least 3 bytes
jb .fail
 
mov eax, [IOCTL.input]
cmp byte [eax], 1 ; 1 means device number and bus number (pci) are given
jne .fail ; other types arent supported for this card yet
 
; check if the device is already listed
 
mov esi, device_list
mov ecx, [devices]
test ecx, ecx
jz .firstdevice
 
; mov eax, [IOCTL.input] ; get the pci bus and device numbers
mov ax , [eax+1] ;
.nextdevice:
mov ebx, [esi]
cmp al, byte[device.pci_bus]
jne @f
cmp ah, byte[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_struct, .fail ; Allocate the buffer for device structure
 
; Fill in the direct call addresses into the struct
 
mov [device.reset], reset
mov [device.transmit], transmit
mov [device.unload], unload
mov [device.name], my_service
 
; save the pci bus and device numbers
 
mov eax, [IOCTL.input]
movzx ecx, byte[eax+1]
mov [device.pci_bus], ecx
movzx ecx, byte[eax+2]
mov [device.pci_dev], ecx
 
; Now, it's time to find the base io addres of the PCI device
 
PCI_find_io
 
; We've found the io address, find IRQ now
 
PCI_find_irq
 
DEBUGF 2,"Hooking into device, dev:%x, bus:%x, irq:%x, addr:%x\n",\
[device.pci_dev]:1,[device.pci_bus]:1,[device.irq_line]:1,[device.io_addr]:4
 
; Ok, the eth_device structure is ready, let's probe the device
 
pushf
cli ; disable ints until initialisation is done
 
call probe ; this function will output in eax
test eax, eax
jnz .err ; If an error occured, exit
 
mov eax, [devices] ; Add the device structure to our device list
mov [device_list+4*eax], ebx ; (IRQ handler uses this list to find device)
inc [devices] ;
 
popf
 
mov [device.type], NET_TYPE_ETH
call NetRegDev
 
cmp eax, -1
je .err
 
ret
 
; If the device was already loaded, find the device number and return it in eax
 
.find_devicenum:
DEBUGF 2,"Trying to find device number of already registered device\n"
call NetPtrToNum ; This kernel procedure converts a pointer to device struct in ebx
; into a device number in edi
mov eax, edi ; Application wants it in eax instead
DEBUGF 2,"Kernel says: %u\n", eax
ret
 
; If an error occured, remove all allocated data and exit (returning -1 in eax)
 
.err:
stdcall KernelFree, ebx
 
.fail:
or eax, -1
ret
 
;------------------------------------------------------
endp
 
 
 
 
 
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
;; ;;
;; Actual Hardware dependent code starts here ;;
;; ;;
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
 
 
unload:
; TODO: (in this particular order)
;
; - Stop the device
; - Detach int handler
; - Remove device from local list (device_list)
; - call unregister function in kernel
; - Remove all allocated structures and buffers the card used
 
or eax,-1
 
ret
 
 
;-------------
;
; Probe
;
;-------------
 
align 4
probe:
 
DEBUGF 1,"Probing i8255x\n"
 
PCI_make_bus_master
 
;---------------------------
; First, identify the device
 
stdcall PciRead32, [device.pci_bus], [device.pci_dev], PCI_VENDOR_ID ; get device/vendor id
 
DEBUGF 1,"Vendor_id=0x%x\n", ax
 
cmp ax, 0x8086
jne .notfound
shr eax, 16
 
DEBUGF 1,"Device_id=0x%x\n", ax
 
mov ecx, DEVICE_IDs
mov edi, device_id_list
repne scasw
jne .notfound
jmp .found
 
.notfound:
DEBUGF 1,"ERROR: Unsupported device!\n"
or eax, -1
ret
 
.found:
 
call ee_get_width
call MAC_read_eeprom
 
;;; TODO: detect phy
 
 
 
;----------
;
; Reset
;
;----------
 
align 4
reset:
 
movzx eax, [device.irq_line]
DEBUGF 1,"Attaching int handler to irq %x\n", eax:1
stdcall AttachIntHandler, eax, int_handler, dword 0
test eax, eax
jnz @f
DEBUGF 1,"\nCould not attach int handler!\n"
; or eax, -1
; ret
@@:
 
DEBUGF 1,"Resetting %s\n", my_service
 
;---------------
; reset the card
 
set_io 0
set_io reg_port
xor eax, eax ; Software Reset
out dx, eax
 
mov esi, 10
call Sleep ; Give the card time to warm up.
 
;---------------------------------
; Tell device where to store stats
 
lea eax, [lstats]
GetRealAddr
set_io 0
set_io reg_scb_ptr
out dx, eax
 
mov ax, INT_MASK + CU_STATSADDR
set_io reg_scb_cmd
out dx, ax
call cmd_wait
 
;-----------------
; setup RX
 
set_io reg_scb_ptr
xor eax, eax
out dx, eax
 
set_io reg_scb_cmd
mov ax, INT_MASK + RX_ADDR_LOAD
out dx, ax
call cmd_wait
 
;-----------------------------
; Create RX and TX descriptors
 
call create_ring
 
; RX start
 
set_io 0
set_io reg_scb_ptr
mov eax, [device.rx_desc]
GetRealAddr
out dx, eax
 
mov ax, INT_MASK + RX_START
set_io reg_scb_cmd
out dx, ax
call cmd_wait
 
; Set-up TX
 
set_io reg_scb_ptr
xor eax, eax
out dx, eax
 
set_io reg_scb_cmd
mov ax, INT_MASK + CU_CMD_BASE
out dx, ax
call cmd_wait
 
; --------------------
 
mov [confcmd.command], CmdConfigure + Cmdsuspend
mov [confcmd.status], 0
lea eax, [txfd]
GetRealAddr
mov [confcmd.link], eax
 
mov esi, confcmd_data
lea edi, [confcmd.data]
mov ecx, 22
rep movsb
 
mov byte[confcmd.data + 1], 0x88 ; fifo of 8 each
mov byte[confcmd.data + 4], 0
mov byte[confcmd.data + 5], 0x80
mov byte[confcmd.data + 15], 0x48
mov byte[confcmd.data + 19], 0x80
mov byte[confcmd.data + 21], 0x05
 
mov [txfd.command], CmdIASetup
mov [txfd.status], 0
lea eax, [confcmd]
GetRealAddr
mov [txfd.link], eax
 
;;; copy in our MAC
 
lea edi, [txfd.tx_desc_addr]
lea esi, [device.mac]
movsd
movsw
 
set_io reg_scb_ptr
lea eax, [txfd]
GetRealAddr
out dx, eax
 
; Start CU & enable ints
 
set_io reg_scb_cmd
mov ax, CU_START
out dx, ax
call cmd_wait
 
;-----------------------
; build txfd structure (again!)
 
lea eax, [txfd]
GetRealAddr
mov [txfd.link], eax
mov [txfd.count], 0x02208000
lea eax, [txfd.tx_buf_addr0]
GetRealAddr
mov [txfd.tx_desc_addr], eax
 
; Indicate that we have successfully reset the card
 
DEBUGF 1,"Resetting %s complete\n", my_service
 
mov [device.mtu], 1514
 
; Set link state to unknown
mov [device.state], ETH_LINK_UNKOWN
 
xor eax, eax ; indicate that we have successfully reset the card
ret
 
 
align 4
create_ring:
 
DEBUGF 1,"Creating ring\n"
 
;---------------------
; build rxfd structure
 
stdcall KernelAlloc, 2000
mov [device.rx_desc], eax
mov esi, eax
GetRealAddr
mov [esi + rxfd.status], 0x0000
mov [esi + rxfd.command], 0x0000
mov [esi + rxfd.link], eax
mov [esi + rxfd.count], 0
mov [esi + rxfd.size], 1528
 
;-----------------------
; build txfd structure
 
lea eax, [txfd]
GetRealAddr
mov [txfd.link], eax
mov [txfd.count], 0x02208000
lea eax, [txfd.tx_buf_addr0]
GetRealAddr
mov [txfd.tx_desc_addr], eax
 
ret
 
 
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Transmit ;;
;; ;;
;; In: buffer pointer in [esp+4] ;;
;; size of buffer in [esp+8] ;;
;; pointer to device structure in ebx ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
transmit:
 
DEBUGF 1,"Transmitting packet, buffer:%x, size:%u\n", [esp+4], [esp+8]
mov eax, [esp+4]
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 dword [esp+8], 1514
ja .error ; packet is too long
cmp dword [esp+8], 60
jb .error ; packet is too short
 
;;; TODO: check if current descriptor is in use
; fill in buffer address and size
mov eax, [esp+4]
mov [last_tx_buffer], eax ;;; FIXME
GetRealAddr
mov [txfd.tx_buf_addr0], eax
mov eax, [esp+8]
mov [txfd.tx_buf_size0], eax
 
mov [txfd.status], 0
mov [txfd.command], Cmdsuspend + CmdTx + CmdTxFlex + 1 shl 15 ;;; EL bit
 
; mov [txfd.count], 0x02208000 ;;;;;;;;;;;
 
; Inform device of the new/updated transmit descriptor
lea eax, [txfd]
GetRealAddr
set_io 0
set_io reg_scb_ptr
out dx, eax
 
; Start the transmit
mov ax, CU_START
set_io reg_scb_cmd
out dx, ax
call cmd_wait
 
; set_io 0 ;; why?
; in ax, dx ;;
;
; @@:
; cmp [txfd.status], 0 ; wait for completion? dont seems a good idea to me..
; je @r
;
; set_io 0 ;; why?
; in ax, dx ;;
 
; Update stats
inc [device.packets_tx]
mov eax, [esp + 8]
add dword [device.bytes_tx], eax
adc dword [device.bytes_tx + 4], 0
 
DEBUGF 1,"Transmit OK\n"
 
xor eax, eax
ret 8
 
.error:
stdcall KernelFree, [esp+4]
or eax, -1
ret 8
 
;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Interrupt handler ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
int_handler:
 
push ebx esi edi
 
DEBUGF 1,"\n%s int\n", my_service
 
; find pointer of device wich made IRQ occur
 
mov ecx, [devices]
test ecx, ecx
jz .nothing
mov esi, device_list
.nextdevice:
mov ebx, [esi]
 
; set_io 0 ; reg_scb_status = 0
set_io reg_scb_status
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,"Device: %x Status: %x\n", ebx, ax
 
test ax, 1 shl 14 ; did we receive a frame?
jz .no_rx
 
push ax
 
DEBUGF 1,"Receiving\n"
 
push ebx
.rx_loop:
pop ebx
 
mov esi, [device.rx_desc]
cmp [esi + rxfd.status], 0 ; we could also check bits C and OK (bit 15 and 13)
je .nodata
 
DEBUGF 1,"rxfd status=0x%x\n", [esi + rxfd.status]:4
 
movzx ecx, [esi + rxfd.count]
and ecx, 0x3fff
 
push ebx
push .rx_loop
push ecx
add esi, rxfd.packet
push esi
 
; Update stats
add dword [device.bytes_rx], ecx
adc dword [device.bytes_rx + 4], 0
inc dword [device.packets_rx]
 
; allocate new descriptor
 
stdcall KernelAlloc, 2000
mov [device.rx_desc], eax
mov esi, eax
GetRealAddr
mov [esi + rxfd.status], 0x0000
mov [esi + rxfd.command], 0xc000 ; End of list + Suspend
mov [esi + rxfd.link], eax
mov [esi + rxfd.count], 0
mov [esi + rxfd.size], 1528
 
; restart RX
 
set_io 0
set_io reg_scb_ptr
; lea eax, [device.rx_desc]
; GetRealAddr
out dx, eax
 
set_io reg_scb_cmd
mov ax, RX_START
out dx, ax
call cmd_wait
 
; And give packet to kernel
 
jmp Eth_input
 
.nodata:
DEBUGF 1, "no more data\n"
pop ax
 
.no_rx:
 
; Cleanup after TX
cmp [txfd.status], 0
je .done
cmp [last_tx_buffer], 0
je .done
push ax
DEBUGF 1, "Removing packet 0x%x from RAM!\n", [last_tx_buffer]
stdcall KernelFree, [last_tx_buffer]
mov [last_tx_buffer], 0
pop ax
 
.done:
and ax, 00111100b
cmp ax, 00001000b
jne .fail
 
DEBUGF 1, "out of resources!\n"
; Restart the RX
 
; allocate new descriptor
 
stdcall KernelAlloc, 2000
mov [device.rx_desc], eax
mov esi, eax
GetRealAddr
mov [esi + rxfd.status], 0x0000
mov [esi + rxfd.command], 0xc000 ; End of list + Suspend
mov [esi + rxfd.link], eax
mov [esi + rxfd.count], 0
mov [esi + rxfd.size], 1528
 
; restart RX
 
set_io 0
set_io reg_scb_ptr
; lea eax, [device.rx_desc]
; GetRealAddr
out dx, eax
 
set_io reg_scb_cmd
mov ax, RX_START
out dx, ax
call cmd_wait
 
.fail:
pop edi esi ebx
xor eax, eax
inc eax
 
ret
 
 
 
 
align 4
cmd_wait:
 
in al, dx
test al, al
jnz cmd_wait
 
ret
 
 
 
 
 
 
align 4
ee_read: ; esi = address to read
 
DEBUGF 1,"Eeprom read from 0x%x", esi
 
set_io 0
set_io reg_eeprom
 
;-----------------------------------------------------
; Prepend start bit + read opcode to the address field
; and shift it to the very left bits of esi
 
mov cl, 29
sub cl, [device.ee_bus_width]
shl esi, cl
or esi, EE_READ shl 29
 
movzx ecx, [device.ee_bus_width]
add ecx, 3
 
mov al, EE_CS
out dx, al
delay
 
;-----------------------
; Write this to the chip
 
.loop:
mov al, EE_CS + EE_SK
shl esi, 1
jnc @f
or al, EE_DI
@@:
out dx, al
delay
 
and al, not EE_SK
out dx, al
delay
 
loop .loop
 
;------------------------------
; Now read the data from eeprom
 
xor esi, esi
mov ecx, 16
 
.loop2:
shl esi, 1
mov al, EE_CS + EE_SK
out dx, al
delay
 
in al, dx
test al, EE_DO
jz @f
inc esi
@@:
 
mov al, EE_CS
out dx, al
delay
 
loop .loop2
 
;-----------------------
; de-activate the eeprom
 
xor ax, ax
out dx, ax
 
 
DEBUGF 1,"=0x%x\n", esi:4
ret
 
 
 
align 4
ee_write: ; esi = address to write to, di = data
 
DEBUGF 1,"Eeprom write 0x%x to 0x%x\n", di, esi
 
set_io 0
set_io reg_eeprom
 
;-----------------------------------------------------
; Prepend start bit + write opcode to the address field
; and shift it to the very left bits of esi
 
mov cl, 29
sub cl, [device.ee_bus_width]
shl esi, cl
or esi, EE_WRITE shl 29
 
movzx ecx, [device.ee_bus_width]
add ecx, 3
 
mov al, EE_CS ; enable chip
out dx, al
 
;-----------------------
; Write this to the chip
 
.loop:
mov al, EE_CS + EE_SK
shl esi, 1
jnc @f
or al, EE_DI
@@:
out dx, al
delay
 
and al, not EE_SK
out dx, al
delay
 
loop .loop
 
;-----------------------------
; Now write the data to eeprom
 
mov ecx, 16
 
.loop2:
mov al, EE_CS + EE_SK
shl di, 1
jnc @f
or al, EE_DI
@@:
out dx, al
delay
 
and al, not EE_SK
out dx, al
delay
 
loop .loop2
 
;-----------------------
; de-activate the eeprom
 
xor al, al
out dx, al
 
 
ret
 
 
 
align 4
ee_get_width:
 
; DEBUGF 1,"Eeprom get width\n"
 
set_io 0
set_io reg_eeprom
 
mov al, EE_CS ; activate eeprom
out dx, al
delay
 
mov si, EE_READ shl 13
xor ecx, ecx
.loop:
mov al, EE_CS + EE_SK
shl si, 1
jnc @f
or al, EE_DI
@@:
out dx, al
delay
 
and al, not EE_SK
out dx, al
delay
 
inc ecx
 
cmp ecx, 15
jae .give_up
 
in al, dx
test al, EE_DO
jnz .loop
 
.give_up:
xor al, al
out dx, al ; de-activate eeprom
 
sub cl, 3 ; dont count the opcode bits
 
mov [device.ee_bus_width], cl
DEBUGF 1,"Eeprom width=%u bit\n", ecx
 
 
;-----------------------
; de-activate the eeprom
 
xor eax, eax
out dx, eax
 
ret
 
 
 
; cx = phy addr
; dx = phy reg addr
 
; ax = data
 
align 4
mdio_read:
 
DEBUGF 1,"MDIO read\n"
 
shl ecx, 21 ; PHY addr
shl edx, 16 ; PHY reg addr
 
mov eax, ecx
or eax, edx
or eax, 10b shl 26 ; read opcode
 
set_io 0
set_io reg_mdi_ctrl
out dx, eax
 
.wait:
delay
in eax, dx
test eax, 1 shl 28 ; ready bit
jz .wait
 
ret
 
; ax = data
; cx = phy addr
; dx = phy reg addr
 
; ax = data
 
align 4
mdio_write:
 
DEBUGF 1,"MDIO write\n"
 
and eax, 0xffff
 
shl ecx, 21 ; PHY addr
shl edx, 16 ; PHY reg addr
 
or eax, ecx
or eax, edx
or eax, 01b shl 26 ; write opcode
 
set_io 0
set_io reg_mdi_ctrl
out dx, eax
 
.wait:
delay
in eax, dx
test eax, 1 shl 28 ; ready bit
jz .wait
 
ret
 
read_mac:
 
ret
 
 
 
align 4
MAC_read_eeprom:
 
mov esi, 0
call ee_read
mov word[device.mac], si
 
mov esi, 1
call ee_read
mov word[device.mac+2], si
 
mov esi, 2
call ee_read
mov word[device.mac+4], si
 
 
ret
 
 
align 4
MAC_write:
 
;;;;
 
ret
 
 
 
 
; End of code
 
align 4 ; Place all initialised data here
 
devices dd 0 ; number of currently running devices
version dd (DRIVER_VERSION shl 16) or (API_VERSION and 0xFFFF)
my_service db 'i8255x', 0 ; max 16 chars include zero
devicename db 'Intel Etherexpress pro/100', 0
 
confcmd_data db 22, 0x08, 0, 0, 0, 0x80, 0x32, 0x03, 1
db 0, 0x2e, 0, 0x60, 0, 0xf2, 0x48, 0, 0x40, 0xf2
db 0x80, 0x3f, 0x05 ; 22 bytes total
 
 
device_id_list:
 
dw 0x1029
dw 0x1030
dw 0x1031
dw 0x1032
dw 0x1033
dw 0x1034
dw 0x1038
dw 0x1039
dw 0x103A
dw 0x103B
dw 0x103C
dw 0x103D
dw 0x103E
dw 0x1050
dw 0x1051
dw 0x1052
dw 0x1053
dw 0x1054
dw 0x1055
dw 0x1056
dw 0x1057
dw 0x1059
dw 0x1064
dw 0x1065
dw 0x1066
dw 0x1067
dw 0x1068
dw 0x1069
dw 0x106A
dw 0x106B
dw 0x1091
dw 0x1092
dw 0x1093
dw 0x1094
dw 0x1095
dw 0x10fe
dw 0x1209
dw 0x1229
dw 0x2449
dw 0x2459
dw 0x245D
dw 0x27DC
 
DEVICE_IDs = ($ - device_id_list) / 2
 
mac_82557_D100_A = 0
mac_82557_D100_B = 1
mac_82557_D100_C = 2
mac_82558_D101_A4 = 4
mac_82558_D101_B0 = 5
mac_82559_D101M = 8
mac_82559_D101S = 9
mac_82550_D102 = 12
mac_82550_D102_C = 13
mac_82551_E = 14
mac_82551_F = 15
mac_82551_10 = 16
mac_unknown = 0xFF
 
phy_100a = 0x000003E0
phy_100c = 0x035002A8
phy_82555_tx = 0x015002A8
phy_nsc_tx = 0x5C002000
phy_82562_et = 0x033002A8
phy_82562_em = 0x032002A8
phy_82562_ek = 0x031002A8
phy_82562_eh = 0x017002A8
phy_82552_v = 0xd061004d
phy_unknown = 0xFFFFFFFF
 
 
include_debug_strings ; All data wich FDO uses will be included here
 
section '.data' data readable writable align 16 ; place all uninitialized data place here
 
device_list rd MAX_DEVICES ; This list contains all pointers to device structures the driver is handling
 
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
/drivers/ethernet/mtd80x.asm
0,0 → 1,1266
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; MTD80x driver for KolibriOS ;;
;; ;;
;; Based on mtd80x.c from the etherboot project ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
format MS COFF
 
API_VERSION = 0x01000100
DRIVER_VERSION = 5
 
MAX_DEVICES = 16
 
DEBUG = 1
__DEBUG__ = 1
__DEBUG_LEVEL__ = 2
 
NUM_TX_DESC = 4
NUM_RX_DESC = 4
 
include '../proc32.inc'
include '../imports.inc'
include '../fdo.inc'
include '../netdrv.inc'
 
public START
public service_proc
public version
 
 
; for different PHY
 
MysonPHY = 1
AhdocPHY = 2
SeeqPHY = 3
MarvellPHY = 4
Myson981 = 5
LevelOnePHY = 6
OtherPHY = 10
 
; Offsets to the Command and Status Registers.
 
PAR0 = 0x0 ; physical address 0-3
PAR1 = 0x04 ; physical address 4-5
MAR0 = 0x08 ; multicast address 0-3
MAR1 = 0x0C ; multicast address 4-7
FAR0 = 0x10 ; flow-control address 0-3
FAR1 = 0x14 ; flow-control address 4-5
TCRRCR = 0x18 ; receive & transmit configuration
BCR = 0x1C ; bus command
TXPDR = 0x20 ; transmit polling demand
RXPDR = 0x24 ; receive polling demand
RXCWP = 0x28 ; receive current word pointer
TXLBA = 0x2C ; transmit list base address
RXLBA = 0x30 ; receive list base address
ISR = 0x34 ; interrupt status
IMR = 0x38 ; interrupt mask
FTH = 0x3C ; flow control high/low threshold
MANAGEMENT = 0x40 ; bootrom/eeprom and mii management
TALLY = 0x44 ; tally counters for crc and mpa
TSR = 0x48 ; tally counter for transmit status
BMCRSR = 0x4c ; basic mode control and status
PHYIDENTIFIER = 0x50 ; phy identifier
ANARANLPAR = 0x54 ; auto-negotiation advertisement and link partner ability
ANEROCR = 0x58 ; auto-negotiation expansion and pci conf.
BPREMRPSR = 0x5c ; bypass & receive error mask and phy status
 
; Bits in the interrupt status/enable registers.
 
RFCON = 0x00020000 ; receive flow control xon packet
RFCOFF = 0x00010000 ; receive flow control xoff packet
LSCStatus = 0x00008000 ; link status change
ANCStatus = 0x00004000 ; autonegotiation completed
FBE = 0x00002000 ; fatal bus error
FBEMask = 0x00001800 ; mask bit12-11
ParityErr = 0x00000000 ; parity error
TargetErr = 0x00001000 ; target abort
MasterErr = 0x00000800 ; master error
TUNF = 0x00000400 ; transmit underflow
ROVF = 0x00000200 ; receive overflow
ETI = 0x00000100 ; transmit early int
ERI = 0x00000080 ; receive early int
CNTOVF = 0x00000040 ; counter overflow
RBU = 0x00000020 ; receive buffer unavailable
TBU = 0x00000010 ; transmit buffer unavilable
TI = 0x00000008 ; transmit interrupt
RI = 0x00000004 ; receive interrupt
RxErr = 0x00000002 ; receive error
 
; Bits in the NetworkConfig register.
 
RxModeMask = 0xe0
AcceptAllPhys = 0x80 ; promiscuous mode
AcceptBroadcast = 0x40 ; accept broadcast
AcceptMulticast = 0x20 ; accept mutlicast
AcceptRunt = 0x08 ; receive runt pkt
ALP = 0x04 ; receive long pkt
AcceptErr = 0x02 ; receive error pkt
 
AcceptMyPhys = 0x00000000
RxEnable = 0x00000001
RxFlowCtrl = 0x00002000
TxEnable = 0x00040000
TxModeFDX = 0x00100000
TxThreshold = 0x00e00000
 
PS1000 = 0x00010000
PS10 = 0x00080000
FD = 0x00100000
 
 
; Bits in network_desc.status
 
RXOWN = 0x80000000 ; own bit
FLNGMASK = 0x0fff0000 ; frame length
FLNGShift = 16
MARSTATUS = 0x00004000 ; multicast address received
BARSTATUS = 0x00002000 ; broadcast address received
PHYSTATUS = 0x00001000 ; physical address received
RXFSD = 0x00000800 ; first descriptor
RXLSD = 0x00000400 ; last descriptor
ErrorSummary = 0x80 ; error summary
RUNT = 0x40 ; runt packet received
LONG = 0x20 ; long packet received
FAE = 0x10 ; frame align error
CRC = 0x08 ; crc error
RXER = 0x04 ; receive error
 
; rx_desc_control_bits
 
RXIC = 0x00800000 ; interrupt control
RBSShift = 0
 
; tx_desc_status_bits
 
TXOWN = 0x80000000 ; own bit
JABTO = 0x00004000 ; jabber timeout
CSL = 0x00002000 ; carrier sense lost
LC = 0x00001000 ; late collision
EC = 0x00000800 ; excessive collision
UDF = 0x00000400 ; fifo underflow
DFR = 0x00000200 ; deferred
HF = 0x00000100 ; heartbeat fail
NCRMask = 0x000000ff ; collision retry count
NCRShift = 0
 
; tx_desc_control_bits
 
TXIC = 0x80000000 ; interrupt control
ETIControl = 0x40000000 ; early transmit interrupt
TXLD = 0x20000000 ; last descriptor
TXFD = 0x10000000 ; first descriptor
CRCEnable = 0x08000000 ; crc control
PADEnable = 0x04000000 ; padding control
RetryTxLC = 0x02000000 ; retry late collision
PKTSMask = 0x3ff800 ; packet size bit21-11
PKTSShift = 11
TBSMask = 0x000007ff ; transmit buffer bit 10-0
TBSShift = 0
 
; BootROM/EEPROM/MII Management Register
 
MASK_MIIR_MII_READ = 0x00000000
MASK_MIIR_MII_WRITE = 0x00000008
MASK_MIIR_MII_MDO = 0x00000004
MASK_MIIR_MII_MDI = 0x00000002
MASK_MIIR_MII_MDC = 0x00000001
 
; ST+OP+PHYAD+REGAD+TA
 
OP_READ = 0x6000 ; ST:01+OP:10+PHYAD+REGAD+TA:Z0
OP_WRITE = 0x5002 ; ST:01+OP:01+PHYAD+REGAD+TA:10
 
; -------------------------------------------------------------------------
; Constants for Myson PHY
; -------------------------------------------------------------------------
 
MysonPHYID = 0xd0000302
MysonPHYID0 = 0x0302
StatusRegister = 18
SPEED100 = 0x0400 ; bit10
FULLMODE = 0x0800 ; bit11
 
; -------------------------------------------------------------------------
; Constants for Seeq 80225 PHY
; -------------------------------------------------------------------------
 
SeeqPHYID0 = 0x0016
MIIRegister18 = 18
SPD_DET_100 = 0x80
DPLX_DET_FULL = 0x40
 
; -------------------------------------------------------------------------
; Constants for Ahdoc 101 PHY
; -------------------------------------------------------------------------
 
AhdocPHYID0 = 0x0022
DiagnosticReg = 18
DPLX_FULL = 0x0800
Speed_100 = 0x0400
 
; --------------------------------------------------------------------------
; Constants
; --------------------------------------------------------------------------
 
MarvellPHYID0 = 0x0141
LevelOnePHYID0 = 0x0013
 
MII1000BaseTControlReg = 9
MII1000BaseTStatusReg = 10
SpecificReg = 17
 
; for 1000BaseT Control Register
 
PHYAbletoPerform1000FullDuplex = 0x0200
PHYAbletoPerform1000HalfDuplex = 0x0100
PHY1000AbilityMask = 0x300
 
; for phy specific status register, marvell phy.
 
SpeedMask = 0x0c000
Speed_1000M = 0x08000
Speed_100M = 0x4000
Speed_10M = 0
Full_Duplex = 0x2000
 
; for phy specific status register, levelone phy
 
LXT1000_100M = 0x08000
LXT1000_1000M = 0x0c000
LXT1000_Full = 0x200
 
; for PHY
 
LinkIsUp = 0x0004
LinkIsUp2 = 0x00040000
 
 
 
virtual at 0
 
mtd_desc:
.status dd ?
.control dd ?
.buffer dd ?
.next_desc dd ?
 
.next_desc_logical dd ?
.skbuff dd ?
 
.reserved1 dd ?
.reserved2 dd ?
 
.size = $
 
end virtual
 
 
virtual at ebx
 
device:
 
ETH_DEVICE
 
.tx_desc rb NUM_TX_DESC*mtd_desc.size
.rx_desc rb NUM_RX_DESC*mtd_desc.size
 
.io_addr dd ?
.pci_bus dd ?
.pci_dev dd ?
.irq_line db ?
.dev_id dw ?
 
.flags dd ?
 
.crvalue dd ?
.bcrvalue dd ?
 
.cur_rx dd ?
.cur_tx dd ?
 
; These values are keep track of the transceiver/media in use.
 
.linkok dd ?
.line_speed dd ?
.duplexmode dd ?
.default_port dd ?
.PHYType dd ?
 
; MII transceiver section.
 
.mii_cnt dd ? ; MII device addresses.
.phys db ? ; MII device addresses.
 
device_size = $ - device
 
end virtual
 
 
 
section '.flat' code readable align 16
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; proc START ;;
;; ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
proc START stdcall, state:dword
 
cmp [state], 1
jne .exit
 
.entry:
 
DEBUGF 2,"Loading %s driver\n", my_service
stdcall RegService, my_service, service_proc
ret
 
.fail:
.exit:
xor eax, eax
ret
 
endp
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; proc SERVICE_PROC ;;
;; ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
proc service_proc stdcall, ioctl:dword
 
mov edx, [ioctl]
mov eax, [IOCTL.io_code]
 
;------------------------------------------------------
 
cmp eax, 0 ;SRV_GETVERSION
jne @F
 
cmp [IOCTL.out_size], 4
jb .fail
mov eax, [IOCTL.output]
mov [eax], dword API_VERSION
 
xor eax, eax
ret
 
;------------------------------------------------------
@@:
cmp eax, 1 ;SRV_HOOK
jne .fail
 
cmp [IOCTL.inp_size], 3 ; Data input must be at least 3 bytes
jb .fail
 
mov eax, [IOCTL.input]
cmp byte [eax], 1 ; 1 means device number and bus number (pci) are given
jne .fail ; other types arent supported for this card yet
 
; check if the device is already listed
 
mov esi, device_list
mov ecx, [devices]
test ecx, ecx
jz .firstdevice
 
; mov eax, [IOCTL.input] ; get the pci bus and device numbers
mov ax , [eax+1] ;
.nextdevice:
mov ebx, [esi]
cmp al, byte[device.pci_bus]
jne @f
cmp ah, byte[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, device_size, .fail
 
; Fill in the direct call addresses into the struct
 
mov [device.reset], reset
mov [device.transmit], transmit
mov [device.unload], unload
mov [device.name], my_service
 
; save the pci bus and device numbers
 
mov eax, [IOCTL.input]
movzx ecx, byte[eax+1]
mov [device.pci_bus], ecx
movzx ecx, byte[eax+2]
mov [device.pci_dev], ecx
 
; Now, it's time to find the base io addres of the PCI device
 
PCI_find_io
 
; We've found the io address, find IRQ now
 
PCI_find_irq
 
DEBUGF 2,"Hooking into device, dev:%x, bus:%x, irq:%x, addr:%x\n",\
[device.pci_dev]:1,[device.pci_bus]:1,[device.irq_line]:1,[device.io_addr]:8
 
; 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 .err2 ; If an error occured, exit
 
mov [device.type], NET_TYPE_ETH
call NetRegDev
 
cmp eax, -1
je .destroy
 
ret
 
; If the device was already loaded, find the device number and return it in eax
 
.find_devicenum:
DEBUGF 2,"Trying to find device number of already registered device\n"
call NetPtrToNum ; This kernel procedure converts a pointer to device struct in ebx
; into a device number in edi
mov eax, edi ; Application wants it in eax instead
DEBUGF 2,"Kernel says: %u\n", eax
ret
 
; If an error occured, remove all allocated data and exit (returning -1 in eax)
 
.destroy:
; todo: reset device into virgin state
 
.err2:
dec [devices]
.err:
DEBUGF 2,"removing device structure\n"
stdcall KernelFree, ebx
.fail:
or eax, -1
ret
 
;------------------------------------------------------
endp
 
 
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
;; ;;
;; Actual Hardware dependent code starts here ;;
;; ;;
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
 
 
 
align 4
unload:
; TODO: (in this particular order)
;
; - Stop the device
 
; /* Disable Tx Rx*/
; outl( mtdx.crvalue & (~TxEnable) & (~RxEnable), mtdx.ioaddr + TCRRCR );
;
; /* Reset the chip to erase previous misconfiguration. */
; mtd_reset(nic);
 
; - 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
;
;-------
align 4
probe:
 
DEBUGF 2,"Probing mtd80x device\n"
 
PCI_make_bus_master
 
stdcall PciRead32, [device.pci_bus], [device.pci_dev], 0
 
cmp ax, 0x1516
jne .notfound
shr eax, 16
mov [device.dev_id], ax
 
cmp ax, 0x0800
je .has_mii_xcvr
 
cmp ax, 0x0803
je .has_chip_xcvr
 
cmp ax, 0x0891
je .has_mii_xcvr
 
.notfound:
DEBUGF 1,"Device not supported!\n"
xor eax, eax
dec eax
ret
 
.has_chip_xcvr:
DEBUGF 1,"Device has chip xcvr\n"
 
jmp .xcvr_set
 
.has_mii_xcvr:
DEBUGF 1,"Device has mii xcvr\n"
 
.xcvr_set:
 
call read_mac
 
; Reset the chip to erase previous misconfiguration.
 
set_io 0
set_io BCR
xor eax, eax
inc eax
out dx, eax
 
; find the connected MII xcvrs
 
cmp [device.dev_id], 0x0803
je .is_803
 
; int phy, phy_idx = 0;
;
; for (phy = 1; phy < 32 && phy_idx < 1; phy++) {
; int mii_status = mdio_read(nic, phy, 1);
;
; if (mii_status != 0xffff && mii_status != 0x0000) {
; mtdx.phys[phy_idx] = phy;
;
; DBG ( "%s: MII PHY found at address %d, status "
; "0x%4.4x.\n", mtdx.nic_name, phy, mii_status );
; /* get phy type */
; {
; unsigned int data;
;
; data = mdio_read(nic, mtdx.phys[phy_idx], 2);
; if (data equ= SeeqPHYID0)
; mtdx.PHYType = SeeqPHY;
; else if (data equ= AhdocPHYID0)
; mtdx.PHYType = AhdocPHY;
; else if (data equ= MarvellPHYID0)
; mtdx.PHYType = MarvellPHY;
; else if (data equ= MysonPHYID0)
; mtdx.PHYType = Myson981;
; else if (data equ= LevelOnePHYID0)
; mtdx.PHYType = LevelOnePHY;
; else
; mtdx.PHYType = OtherPHY;
; }
; phy_idx++;
; }
; }
;
; mtdx.mii_cnt = phy_idx;
; if (phy_idx equ= 0) {
; printf("%s: MII PHY not found -- this device may "
; "not operate correctly.\n", mtdx.nic_name);
; }
 
jmp .no_803
 
.is_803:
 
mov [device.phys], 32
 
; get phy type
set_io 0
set_io PHYIDENTIFIER
in eax, dx
 
cmp eax, MysonPHYID
jne @f
 
mov [device.PHYType], MysonPHY
DEBUGF 1,"MysonPHY\n"
jmp .no_803
 
@@:
mov [device.PHYType], OtherPHY
DEBUGF 1,"OtherPHY\n"
 
.no_803:
 
;-------
;
; RESET
;
;-------
align 4
reset:
 
DEBUGF 1,"Resetting mtd80x\n"
 
;--------------------------------
; insert irq handler on given irq
 
movzx eax, [device.irq_line]
DEBUGF 1,"Attaching int handler to irq %x\n", eax:1
stdcall AttachIntHandler, eax, int_handler, dword 0
test eax, eax
jnz @f
DEBUGF 1,"\nCould not attach int handler!\n"
; or eax, -1
; ret
@@:
 
; Reset the chip to erase previous misconfiguration.
 
set_io 0
set_io BCR
xor eax, eax
inc eax
out dx, eax
 
call init_ring
 
; Initialize other registers.
; Configure the PCI bus bursts and FIFO thresholds.
 
mov [device.bcrvalue], 0x10 ; little-endian, 8 burst length
mov [device.crvalue], 0xa00 ; 128 burst length
 
cmp [device.dev_id], 0x891
jne @f
or [device.bcrvalue], 0x200 ; set PROG bit
or [device.crvalue], 0x02000000 ; set enhanced bit
@@:
 
or [device.crvalue], RxEnable + TxThreshold + TxEnable
 
call set_rx_mode
 
set_io 0
set_io BCR
mov eax, [device.bcrvalue]
out dx, eax
 
set_io TCRRCR
mov eax, [device.crvalue]
out dx, eax
 
call getlinkstatus
call getlinktype
 
; Restart Rx engine if stopped.
 
set_io 0
set_io RXPDR
xor eax, eax
out dx, eax
 
; Enable interrupts
 
set_io 0
set_io ISR
mov eax, (FBE or TUNF or CNTOVF or RBU or TI or RI)
out dx, eax
 
set_io IMR
; mov eax, (FBE or TUNF or CNTOVF or RBU or TI or RI)
out dx, eax
 
; clear packet/byte counters
 
xor eax, eax
lea edi, [device.bytes_tx]
mov ecx, 6
rep stosd
 
mov [device.mtu], 1514
 
; Set link state to unknown
mov [device.state], ETH_LINK_UNKOWN
 
xor eax, eax
ret
 
 
 
 
align 4
init_ring:
 
DEBUGF 1,"initializing rx and tx ring\n"
 
; Initialize all Rx descriptors
 
lea esi, [device.rx_desc]
mov [device.cur_rx], esi
mov ecx, NUM_RX_DESC
.rx_desc_loop:
mov [esi + mtd_desc.status], RXOWN
mov [esi + mtd_desc.control], 1536 shl RBSShift
 
lea eax, [esi + mtd_desc.size]
mov [esi + mtd_desc.next_desc_logical], eax
push ecx esi
GetRealAddr
mov [esi + mtd_desc.next_desc], eax
 
stdcall KernelAlloc, 1536
pop esi
push esi
mov [esi + mtd_desc.skbuff], eax
call GetPgAddr
pop esi ecx
mov [esi + mtd_desc.buffer], eax
 
add esi, mtd_desc.size
loop .rx_desc_loop
 
; Mark the last entry as wrapping the ring.
 
lea eax, [device.rx_desc]
mov [esi - mtd_desc.size + mtd_desc.next_desc_logical], eax
push esi
GetRealAddr
pop esi
mov [esi - mtd_desc.size + mtd_desc.next_desc], eax
 
set_io 0
set_io RXLBA
out dx, eax
 
; Initialize all Tx descriptors
 
lea esi, [device.tx_desc]
mov [device.cur_tx], esi
mov ecx, NUM_TX_DESC
.tx_desc_loop:
mov [esi + mtd_desc.status], 0
 
lea eax, [esi + mtd_desc.size]
mov [esi + mtd_desc.next_desc_logical], eax
push ecx esi
GetRealAddr
pop esi ecx
mov [esi + mtd_desc.next_desc], eax
 
add esi, mtd_desc.size
loop .tx_desc_loop
 
; Mark the last entry as wrapping the ring.
 
lea eax, [device.tx_desc]
mov [esi - mtd_desc.size + mtd_desc.next_desc_logical], eax
push esi
GetRealAddr
pop esi
mov [esi - mtd_desc.size + mtd_desc.next_desc], eax
 
set_io 0
set_io TXLBA
out dx, eax
 
ret
 
 
align 4
set_rx_mode:
 
DEBUGF 1,"Setting RX mode\n"
 
; Too many to match, or accept all multicasts.
 
set_io 0
set_io MAR0
xor eax, eax
not eax
out dx, eax
set_io MAR1
out dx, eax
 
and [device.crvalue], not (RxModeMask)
or [device.crvalue], AcceptBroadcast + AcceptMulticast + AcceptMyPhys
 
ret
 
 
align 4
getlinkstatus:
 
DEBUGF 1,"Getting link status\n"
 
mov [device.linkok], 0
 
cmp [device.PHYType], MysonPHY
jne .no_myson_phy
 
set_io 0
set_io BMCRSR
mov ecx, 1000
.loop1:
in eax, dx
test eax, LinkIsUp2
jnz .link_ok
 
push ecx edx ebx
mov esi, 10
call Sleep
pop ebx edx ecx
loop .loop1
 
ret
 
.no_myson_phy:
 
; for (i = 0; i < DelayTime; ++i) {
; if (mdio_read(nic, mtdx.phys[0], MII_BMSR) & BMSR_LSTATUS) {
; mtdx.linkok = 1;
; return;
; }
; m80x_delay(100);
 
ret
 
.link_ok:
DEBUGF 1,"Link is up\n"
inc [device.linkok]
ret
 
 
 
 
align 4
getlinktype:
 
DEBUGF 1,"Getting link type\n"
 
cmp [device.PHYType], MysonPHY
jne .no_myson_phy
 
DEBUGF 1,"myson PHY\n"
 
set_io 0
set_io TCRRCR
in eax, dx
 
mov [device.duplexmode], 1 ; 1 = half duplex
test eax, FD
jne @f
DEBUGF 1,"full duplex\n"
inc [device.duplexmode] ; 2 = full duplex
@@:
 
mov [device.line_speed], 1 ; 1 = 10M
test eax, PS10
jne @f
DEBUGF 1,"100mbit\n"
inc [device.line_speed] ; 2 = 100M
@@:
 
ret
 
.no_myson_phy:
 
DEBUGF 1,"no myson phy\n"
 
; if (mtdx.PHYType equ= SeeqPHY) { /* this PHY is SEEQ 80225 */
; unsigned int data;
;
; data = mdio_read(dev, mtdx.phys[0], MIIRegister18);
; if (data & SPD_DET_100)
; mtdx.line_speed = 2; /* 100M */
; else
; mtdx.line_speed = 1; /* 10M */
; if (data & DPLX_DET_FULL)
; mtdx.duplexmode = 2; /* full duplex mode */
; else
; mtdx.duplexmode = 1; /* half duplex mode */
; } else if (mtdx.PHYType equ= AhdocPHY) {
; unsigned int data;
;
; data = mdio_read(dev, mtdx.phys[0], DiagnosticReg);
; if (data & Speed_100)
; mtdx.line_speed = 2; /* 100M */
; else
; mtdx.line_speed = 1; /* 10M */
; if (data & DPLX_FULL)
; mtdx.duplexmode = 2; /* full duplex mode */
; else
; mtdx.duplexmode = 1; /* half duplex mode */
; }
; else if (mtdx.PHYType equ= MarvellPHY) {
; unsigned int data;
;
; data = mdio_read(dev, mtdx.phys[0], SpecificReg);
; if (data & Full_Duplex)
; mtdx.duplexmode = 2; /* full duplex mode */
; else
; mtdx.duplexmode = 1; /* half duplex mode */
; data &= SpeedMask;
; if (data equ= Speed_1000M)
; mtdx.line_speed = 3; /* 1000M */
; else if (data equ= Speed_100M)
; mtdx.line_speed = 2; /* 100M */
; else
; mtdx.line_speed = 1; /* 10M */
; }
; else if (mtdx.PHYType equ= Myson981) {
; unsigned int data;
;
; data = mdio_read(dev, mtdx.phys[0], StatusRegister);
;
; if (data & SPEED100)
; mtdx.line_speed = 2;
; else
; mtdx.line_speed = 1;
;
; if (data & FULLMODE)
; mtdx.duplexmode = 2;
; else
; mtdx.duplexmode = 1;
; }
; else if (mtdx.PHYType equ= LevelOnePHY) {
; unsigned int data;
;
; data = mdio_read(dev, mtdx.phys[0], SpecificReg);
; if (data & LXT1000_Full)
; mtdx.duplexmode = 2; /* full duplex mode */
; else
; mtdx.duplexmode = 1; /* half duplex mode */
; data &= SpeedMask;
; if (data equ= LXT1000_1000M)
; mtdx.line_speed = 3; /* 1000M */
; else if (data equ= LXT1000_100M)
; mtdx.line_speed = 2; /* 100M */
; else
; mtdx.line_speed = 1; /* 10M */
; }
 
; // chage crvalue
; // mtdx.crvalue&equ(~PS10)&(~FD);
; mtdx.crvalue &= (~PS10) & (~FD) & (~PS1000);
; if (mtdx.line_speed equ= 1)
; mtdx.crvalue |= PS10;
; else if (mtdx.line_speed equ= 3)
; mtdx.crvalue |= PS1000;
; if (mtdx.duplexmode equ= 2)
; mtdx.crvalue |= FD;
;
 
ret
 
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Transmit ;;
;; ;;
;; In: buffer pointer in [esp+4] ;;
;; size of buffer in [esp+8] ;;
;; pointer to device structure in ebx ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
transmit:
 
DEBUGF 1,"Transmitting packet, buffer:%x, size:%u\n",[esp+4],[esp+8]
mov eax, [esp+4]
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 dword [esp+8], 1514
ja .fail
 
mov esi, [device.cur_tx]
push [esi + mtd_desc.next_desc_logical]
pop [device.cur_tx]
 
; todo: check if descriptor is not owned by the device!
 
mov eax, [esp + 4]
mov [esi + mtd_desc.skbuff], eax
GetRealAddr
mov [esi + mtd_desc.buffer], eax
 
mov eax, [esp + 8]
shl eax, PKTSShift ; packet size
or eax, TXLD + TXFD + CRCEnable + PADEnable + TXIC + 1536 shl TBSShift ; buffer size
mov [esi + mtd_desc.control], eax
 
mov [esi + mtd_desc.status], TXOWN
 
;-------------
; Update stats
 
inc [device.packets_tx]
mov eax, [esp+8]
add dword [device.bytes_tx], eax
adc dword [device.bytes_tx + 4], 0
 
; Point to transmit descriptor
 
set_io 0
set_io TXLBA
mov eax, esi
GetRealAddr
out dx, eax
 
; set_io TCRRCR
; mov eax, [device.crvalue]
; out dx, eax
 
; Wake the potentially-idle transmit channel.
 
set_io TXPDR ; TX Poll
xor eax, eax
out dx, eax
 
DEBUGF 1,"transmit ok\n"
xor eax, eax
ret 8
 
.fail:
DEBUGF 1,"transmit failed\n"
or eax, -1
stdcall KernelFree, [esp + 4]
ret 8
 
 
 
align 4
read_mac:
 
set_io 0
set_io PAR0
lea edi, [device.mac]
insd
stosd
set_io PAR1
insw
stosw
 
DEBUGF 1,"MAC = %x-%x-%x-%x-%x-%x\n",\
[device.mac+0]:2,[device.mac+1]:2,[device.mac+2]:2,[device.mac+3]:2,[device.mac+4]:2,[device.mac+5]:2
 
ret
 
align 4
write_mac:
 
ret
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Interrupt handler ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
int_handler:
 
push ebx esi edi
 
DEBUGF 1,"\n%s int\n", my_service
 
; find pointer of device wich made IRQ occur
 
mov ecx, [devices]
test ecx, ecx
jz .nothing
mov esi, device_list
.nextdevice:
mov ebx, [esi]
 
set_io 0
set_io ISR
in eax, dx
out dx, eax ; send it back to ACK
test eax, eax
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,"Device: %x Status: %x ", ebx, ax
 
test ax, RI ; receive interrupt
jz .no_rx
 
DEBUGF 1,"Receive interrupt\n"
.rx:
push ax
 
.rx_loop:
mov esi, [device.cur_rx]
 
test [esi + mtd_desc.status], RXOWN
jnz .fail_rx
 
push .rx_complete
 
mov ecx, [esi + mtd_desc.status]
shr ecx, FLNGShift
sub ecx, 4 ; we dont need CRC
push ecx
 
;-------------
; Update stats
 
add dword [device.bytes_rx], ecx
adc dword [device.bytes_rx + 4], 0
inc dword [device.packets_rx]
 
 
push [esi + mtd_desc.skbuff]
 
jmp Eth_input
 
.rx_complete:
mov esi, [device.cur_rx]
 
mov [esi + mtd_desc.control], 1536 shl RBSShift
 
stdcall KernelAlloc, 1536
mov [esi + mtd_desc.skbuff], eax
call GetPgAddr
mov [esi + mtd_desc.buffer], eax
 
mov [esi + mtd_desc.status], RXOWN
 
mov eax, [esi + mtd_desc.next_desc_logical]
mov [device.cur_rx], eax
 
jmp .rx_loop
;
; while( ( mtdx.cur_rx->status & RXOWN ) == 0 )
; {
; mtdx.cur_rx->status = RXOWN;
; mtdx.cur_rx = mtdx.cur_rx->next_desc_logical;
; }
;
; /* Restart Rx engine if stopped. */
; outl(0, mtdx.ioaddr + RXPDR);
 
.fail_rx:
DEBUGF 1,"RX failed\n"
 
pop ax
.no_rx:
 
test ax, TI ; transmit interrupt
jz .no_tx
 
DEBUGF 1,"Transmit interrupt\n"
push ax
 
lea esi, [device.tx_desc]
mov ecx, NUM_TX_DESC
.tx_loop:
 
test [esi + mtd_desc.status], TXOWN
jnz .skip_this_one
 
mov eax, [esi + mtd_desc.skbuff]
test eax, eax
je .skip_this_one
 
mov [esi + mtd_desc.skbuff], 0
 
DEBUGF 1,"freeing buffer:%x\n", eax
stdcall KernelFree, eax
 
.skip_this_one:
mov esi, [esi + mtd_desc.next_desc_logical]
loop .tx_loop
 
pop ax
 
.no_tx:
 
test ax, TBU
jz .no_tbu
 
DEBUGF 1,"Transmit buffer unavailable!\n"
 
.no_tbu:
 
.fail:
pop edi esi ebx
xor eax, eax
inc eax
 
ret
 
 
; End of code
 
align 4 ; Place all initialised data here
 
devices dd 0
version dd (DRIVER_VERSION shl 16) or (API_VERSION and 0xFFFF)
my_service db 'mtd80x',0 ; max 16 chars include zero
 
 
; 0x1516, 0x0800, "MTD800", "Myson MTD800"
; 0x1516, 0x0803, "MTD803", "Surecom EP-320X"
; 0x1516, 0x0891, "MTD891", "Myson MTD891"
 
 
include_debug_strings ; All data wich FDO uses will be included here
 
section '.data' data readable writable align 16 ; place all uninitialized data place here
 
device_list rd MAX_DEVICES ; This list contains all pointers to device structures the driver is handling
 
 
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
/drivers/ethernet/pcnet32.asm
0,0 → 1,1578
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. 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 MS COFF
 
API_VERSION = 0x01000100
 
DEBUG = 1
__DEBUG__ = 1
__DEBUG_LEVEL__ = 2
 
MAX_DEVICES = 4
MAX_ETH_FRAME_SIZE = 1514
 
TX_RING_SIZE = 4
RX_RING_SIZE = 4
 
include '../proc32.inc'
include '../imports.inc'
include '../fdo.inc'
include '../netdrv.inc'
 
public START
public service_proc
public version
 
 
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
 
LOG_TX_BUFFERS = 2 ; FIXME
LOG_RX_BUFFERS = 2
 
TX_RING_MOD_MASK = (TX_RING_SIZE-1)
TX_RING_LEN_BITS = (LOG_TX_BUFFERS shl 12)
 
RX_RING_MOD_MASK = (RX_RING_SIZE-1)
RX_RING_LEN_BITS = (LOG_RX_BUFFERS 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 = 1 shl 0
CSR_START = 1 shl 1
CSR_STOP = 1 shl 2
CSR_TX = 1 shl 3
CSR_TXON = 1 shl 4
CSR_RXON = 1 shl 5
CSR_INTEN = 1 shl 6
CSR_INTR = 1 shl 7
CSR_IDONE = 1 shl 8
CSR_TINT = 1 shl 9
CSR_RINT = 1 shl 10
CSR_MERR = 1 shl 11
CSR_MISS = 1 shl 12
CSR_CERR = 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
 
IMR = IMR_IDONE ; IMR_TINT + IMR_RINT + IMR_MERR + IMR_MISS ;+ IMR_IDONE
 
; 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
 
 
virtual at ebx
 
device:
 
ETH_DEVICE
 
; device specific
 
rb 0x100-(($ - device) and 0xff) ; align 256
.private:
.mode_ dw ?
.tlen_rlen dw ?
.phys_addr dp ?
.reserved dw ?
.filter dq ?
.rx_ring_phys dd ?
.tx_ring_phys dd ?
 
rb 0x100-(($ - device) and 0xff) ; align 256
.rx_ring rb RX_RING_SIZE * descriptor.size
 
rb 0x100-(($ - device) and 0xff) ; align 256
.tx_ring rb TX_RING_SIZE * descriptor.size
 
.cur_rx db ?
.cur_tx db ?
.last_tx db ?
.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 ?
 
.read_csr dd ?
.write_csr dd ?
.read_bcr dd ?
.write_bcr dd ?
.read_rap dd ?
.write_rap dd ?
.sw_reset dd ?
 
device_size = $ - device
 
end virtual
 
struc descriptor {
.base dd ?
.length dw ?
.status dw ?
.msg_length dw ?
.misc dw ?
.virtual dd ?
 
.size:
}
 
virtual at 0
descriptor descriptor
end virtual
 
 
 
 
section '.flat' code readable align 16
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; proc START ;;
;; ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
proc START stdcall, state:dword
 
cmp [state], 1
jne .exit
 
.entry:
 
DEBUGF 1,"Loading %s driver\n", my_service
stdcall RegService, my_service, service_proc
ret
 
.fail:
.exit:
xor eax, eax
ret
 
endp
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; proc SERVICE_PROC ;;
;; ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
proc service_proc stdcall, ioctl:dword
 
mov edx, [ioctl]
mov eax, [IOCTL.io_code]
 
;------------------------------------------------------
 
cmp eax, 0 ;SRV_GETVERSION
jne @F
 
cmp [IOCTL.out_size], 4
jb .fail
mov eax, [IOCTL.output]
mov [eax], dword API_VERSION
 
xor eax, eax
ret
 
;------------------------------------------------------
@@:
cmp eax, 1 ;SRV_HOOK
jne .fail
 
cmp [IOCTL.inp_size], 3 ; Data input must be at least 3 bytes
jb .fail
 
mov eax, [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, [IOCTL.input] ; get the pci bus and device numbers
mov ax , [eax+1] ;
.nextdevice:
mov ebx, [esi]
cmp al, byte[device.pci_bus]
jne @f
cmp ah, byte[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, device_size, .fail
 
; Fill in the direct call addresses into the struct
 
mov [device.reset], reset
mov [device.transmit], transmit
mov [device.unload], unload
mov [device.name], my_service
 
; save the pci bus and device numbers
 
mov eax, [IOCTL.input]
movzx ecx, byte[eax+1]
mov [device.pci_bus], ecx
movzx ecx, byte[eax+2]
mov [device.pci_dev], ecx
 
; Now, it's time to find the base io addres of the PCI device
 
PCI_find_io
 
; We've found the io address, find IRQ now
 
PCI_find_irq
 
DEBUGF 1,"Hooking into device, dev:%x, bus:%x, irq:%x, addr:%x\n",\
[device.pci_dev]:1,[device.pci_bus]:1,[device.irq_line]:1,[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 [device.type], NET_TYPE_ETH
call 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"
call 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
 
dec [devices]
.err:
DEBUGF 1,"Error, removing all data !\n"
stdcall KernelFree, ebx
 
.fail:
or eax, -1
ret
 
;------------------------------------------------------
endp
 
 
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
;; ;;
;; Actual Hardware dependent code starts here ;;
;; ;;
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
 
align 4
unload:
; TODO: (in this particular order)
;
; - Stop the device
; - Detach int handler
; - Remove device from local list (RTL8139_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, [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 1,"PCnet device not found!\n"
mov eax, 1
ret
 
.L1:
mov ecx, CSR_CHIPID0
call [device.read_csr]
 
mov esi, eax
shr esi, 12
 
and ax, 0xfff
cmp ax, 3
jne .no_dev
 
mov ecx, CSR_CHIPID1
call [device.read_csr]
shl eax, 4
or eax, esi
mov [device.chip_version], ax
 
mov [device.fdx], 0
mov [device.mii], 0
mov [device.fset], 0
mov [device.dxsuflo], 0
mov [device.ltint], 0
 
cmp ax, 0x2420
je .L2
cmp ax, 0x2430
je .L2
 
mov [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 1,"Invalid chip rev\n"
jmp .no_dev
.L2:
mov [device.name], device_l2
jmp .L10
.L4:
mov [device.name], device_l4
; mov [device.fdx], 1
jmp .L10
.L5:
mov [device.name], device_l5
; mov [device.fdx], 1
mov [device.mii], 1
mov [device.fset], 1
mov [device.ltint], 1
jmp .L10
.L6:
mov [device.name], device_l6
; mov [device.fdx], 1
mov [device.mii], 1
mov [device.fset], 1
jmp .L10
.L7:
mov [device.name], device_l7
; mov [device.fdx], 1
mov [device.mii], 1
jmp .L10
.L8:
mov [device.name], device_l8
; mov [device.fdx], 1
mov ecx, CSR_RXPOLL
call dword [device.read_bcr]
call dword [device.write_bcr]
jmp .L10
.L9:
mov [device.name], device_l9
; mov [device.fdx], 1
mov [device.mii], 1
.L10:
DEBUGF 1,"device name: %s\n", [device.name]
 
cmp [device.fset], 1
jne .L11
mov ecx, BCR_BUSCTL
call [device.read_bcr]
or eax, 0x800
call [device.write_bcr]
 
mov ecx, CSR_DMACTL
call [device.read_csr]
; and eax, 0xc00
; or eax, 0xc00
mov eax, 0xc00
call [device.write_csr]
 
mov [device.dxsuflo],1
mov [device.ltint],1
.L11:
 
PCI_make_bus_master
 
mov [device.options], PORT_ASEL
mov [device.mode_], MODE_RXD + MODE_TXD ; disable receive and transmit
mov [device.tlen_rlen], (TX_RING_LEN_BITS or RX_RING_LEN_BITS)
 
mov dword [device.filter], 0
mov dword [device.filter+4], 0
 
align 4
reset:
 
; attach int handler
 
movzx eax, [device.irq_line]
DEBUGF 1,"Attaching int handler to irq %x\n", eax:1
stdcall AttachIntHandler, eax, int_handler, dword 0
test eax, eax
jnz @f
DEBUGF 1,"\nCould not attach int handler!\n"
; or eax, -1
; ret
@@:
 
mov edx, [device.io_addr]
 
call [device.sw_reset]
 
; Switch pcnet32 to 32bit mode
mov ecx, BCR_SSTYLE
mov eax, 2
call [device.write_bcr]
 
; set/reset autoselect bit
mov ecx, BCR_MISCCFG
call [device.read_bcr]
and eax, not 2
test [device.options], PORT_ASEL
jz @f
or eax, 2
@@:
call [device.write_bcr]
 
; Handle full duplex setting
cmp byte [device.full_duplex], 0
je .duplex_ok
mov ecx, BCR_DUPLEX
call [device.read_bcr]
and eax, not 3
test [device.options], PORT_FD
jz @f
or eax, 1
cmp [device.options], PORT_FD or PORT_AUI
jne .set_duplex
or eax, 2
jmp .set_duplex
@@:
test [device.options], PORT_ASEL
jz .set_duplex
cmp [device.chip_version], 0x2627
jne .set_duplex
or eax, 3
.set_duplex:
mov ecx, BCR_DUPLEX
call [device.write_bcr]
.duplex_ok:
 
; set/reset GPSI bit in test register
mov ecx, 124
call [device.read_csr]
mov ecx, [device.options]
and ecx, PORT_PORTSEL
cmp ecx, PORT_GPSI
jne @f
or eax, 0x10
@@:
call [device.write_csr]
cmp [device.mii], 0
je .L6
test [device.options], PORT_ASEL
jnz .L6
mov ecx, BCR_MIICTL
call [device.read_bcr]
and eax, not 0x38
test [device.options], PORT_FD
jz @f
or eax, 0x10
@@:
test [device.options], PORT_100
jz @f
or eax, 0x08
@@:
call [device.write_bcr]
jmp .L9
.L6:
test [device.options], PORT_ASEL
jz .L9
mov ecx, BCR_MIICTL
DEBUGF 1,"ASEL, enable auto-negotiation\n"
call [device.read_bcr]
and eax, not 0x98
or eax, 0x20
call [device.write_bcr]
.L9:
cmp [device.ltint], 0
je @f
mov ecx, 5
call [device.read_csr]
or eax, (1 shl 14)
call [device.write_csr]
@@:
mov eax, [device.options]
and eax, PORT_PORTSEL
shl eax, 7
mov [device.mode_], ax
mov dword [device.filter], -1
mov dword [device.filter+4], -1
 
 
 
;-----------------------------
 
test [device.mii], 1
jz .no_mii
 
mov [device.phy], 0
 
.mii_loop:
mov ecx, MII_PHYSID1
call mdio_read
cmp ax, 0xffff
je .next
 
DEBUGF 1, "0x%x\n", ax
 
mov ecx, MII_PHYSID2
call mdio_read
cmp ax, 0xffff
je .next
 
DEBUGF 1, "0x%x\n", ax
 
jmp .got_phy
 
cmp [device.phy], 31
jne .next
mov ax, [device.chip_version]
inc ax
and ax, 0xfffe
cmp ax, 0x2624 ; 79c971 & 79c972 have phantom phy at id 31
je .got_phy
 
.next:
inc [device.phy]
cmp [device.phy], MAX_PHYS
jb .mii_loop
 
DEBUGF 1, "No PHY found!\n"
 
or eax, -1
ret
 
.got_phy:
DEBUGF 1, "Found PHY at 0x%x\n", [device.phy]:4
 
.no_mii:
 
;-----------------------------------------------
 
call read_mac
 
lea esi, [device.mac]
lea edi, [device.phys_addr]
movsd
movsw
 
call init_ring
 
mov edx, [device.io_addr] ; init ring destroys edx
 
lea eax, [device.private]
GetRealAddr
push eax
and eax, 0xffff
mov ecx, 1
call [device.write_csr]
pop eax
shr eax, 16
mov ecx, 2
call [device.write_csr]
 
mov ecx, 4
mov eax, 0x0915
call [device.write_csr]
 
; Set the interrupt mask
mov ecx, CSR_IMR
mov eax, IMR
call [device.write_csr]
 
; Initialise the device
xor ecx, ecx
mov eax, CSR_INIT
call [device.write_csr]
 
mov esi, 100
; xor ecx, ecx
@@:
call [device.read_csr]
test ax, CSR_IDONE
jnz @f
 
dec esi
jnz @r
DEBUGF 1,"Initialize timeout!\n"
@@:
 
; Start the device and enable interrupts
xor ecx, ecx
mov eax, CSR_START + CSR_INTEN
call [device.write_csr]
 
; Set the mtu, kernel will be able to send now
mov [device.mtu], 1514
 
; get link status
mov [device.state], ETH_LINK_UNKOWN
 
call check_media
 
DEBUGF 1,"reset complete\n"
xor eax, eax
ret
 
 
align 4
init_ring:
 
DEBUGF 1,"init ring\n"
 
lea edi, [device.rx_ring]
mov eax, edi
GetRealAddr
mov [device.rx_ring_phys], eax
mov ecx, RX_RING_SIZE
.rx_init:
push ecx
stdcall KernelAlloc, PKT_BUF_SZ
pop ecx
mov [edi + descriptor.virtual], eax
GetRealAddr
mov [edi + descriptor.base], eax
mov [edi + descriptor.length], - PKT_BUF_SZ
mov [edi + descriptor.status], RXSTAT_OWN
mov dword [edi + descriptor.msg_length], 0 ; also clears misc field
add edi, descriptor.size
dec ecx
jnz .rx_init
 
lea edi, [device.tx_ring]
mov eax, edi
GetRealAddr
mov [device.tx_ring_phys], eax
mov ecx, TX_RING_SIZE
.tx_init:
mov [edi + descriptor.status], 0
add edi, descriptor.size
dec ecx
jnz .tx_init
 
mov [device.tlen_rlen], (TX_RING_LEN_BITS or RX_RING_LEN_BITS)
 
mov [device.cur_tx], 0
mov [device.last_tx], 0
mov [device.cur_rx], 0
 
ret
 
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Transmit ;;
;; ;;
;; In: buffer pointer in [esp+4] ;;
;; size of buffer in [esp+8] ;;
;; pointer to device structure in ebx ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
transmit:
DEBUGF 1,"Transmitting packet, buffer:%x, size:%u\n", [esp+4], [esp+8]
mov eax, [esp+4]
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 dword [esp+8], 1514
ja .nospace ; packet is too long
cmp dword [esp+8], 60
jb .nospace ; packet is too short
 
; check descriptor
lea edi, [device.tx_ring]
movzx eax, [device.cur_tx]
shl eax, 4
add edi, eax
 
test [edi + descriptor.status], TXCTL_OWN
jnz .nospace
; descriptor is free, use it
mov eax, [esp+4]
mov [edi + descriptor.virtual], eax
GetRealAddr
mov [edi + descriptor.base], eax
; set length
mov eax, [esp+8]
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, [device.io_addr]
xor ecx, ecx ; CSR0
call [device.read_csr]
or eax, CSR_TX
call [device.write_csr]
 
; get next descriptor 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, ...
inc [device.cur_tx]
and [device.cur_tx], TX_RING_SIZE - 1
DEBUGF 2," - Packet Sent! "
 
; Update stats
inc [device.packets_tx]
mov eax, [esp+8]
add dword [device.bytes_tx], eax
adc dword [device.bytes_tx + 4], 0
 
.finish:
DEBUGF 2," - Done!\n"
xor eax, eax
ret 8
 
.nospace:
DEBUGF 1, 'ERROR: no free transmit descriptors\n'
stdcall KernelFree, [esp+4]
or eax, -1
ret 8
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Interrupt handler ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
int_handler:
 
push ebx esi edi
 
DEBUGF 1,"\n%s int\n", my_service
 
; 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, [device.io_addr]
push ecx
xor ecx, ecx ; CSR0
call [device.read_csr] ; get IRQ reason
call [device.write_csr] ; write it back to ACK
pop ecx
and 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:2
 
push ax
test ax, CSR_RINT
jz .not_receive
 
push ebx
.rx_loop:
pop ebx
movzx eax, [device.cur_rx]
shl eax, 4
lea edi, [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 .not_receive
 
test ax, RXSTAT_ENP
jz .not_receive
 
test ax, RXSTAT_STP
jz .not_receive
 
movzx ecx, [edi + descriptor.msg_length] ; get packet length in ecx
sub ecx, 4 ;
 
; Set pointers for ETH_input
push ebx
 
push .rx_loop ; return address
push ecx ; packet size
push [edi + descriptor.virtual] ; packet address
 
; Update stats
add dword [device.bytes_rx], ecx
adc dword [device.bytes_rx + 4], 0
inc [device.packets_rx]
 
; now allocate a new buffer
stdcall KernelAlloc, PKT_BUF_SZ ; Allocate a buffer for the next packet
mov [edi + descriptor.virtual], eax ; set virtual address
GetRealAddr
mov [edi + descriptor.base], eax ; and real address
 
; mov word [edi + descriptor.length], - PKT_BUF_SZ
mov [edi + descriptor.status], RXSTAT_OWN ; give it back to PCnet controller
 
inc [device.cur_rx] ; set next receive descriptor
and [device.cur_rx], RX_RING_SIZE - 1
 
jmp Eth_input
 
.not_receive:
pop ax
 
test ax, CSR_TINT
jz .not_transmit
 
.tx_loop:
lea edi, [device.tx_ring]
movzx eax, [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
 
stdcall KernelFree, eax
 
inc [device.last_tx]
and [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",[esp+0]:2,[esp+1]:2,[esp+2]:2,[esp+3]:2,[esp+4]:2,[esp+5]:2
 
mov edx, [device.io_addr]
add dx, 2
xor eax, eax
 
mov ecx, CSR_PAR0
@@:
pop ax
call [device.write_csr]
DEBUGF 1,"."
inc ecx
cmp ecx, CSR_PAR2
jb @r
 
DEBUGF 1,"\n"
 
; Notice this procedure does not ret, but continues to read_mac instead.
 
;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Read MAC address ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;
align 4
read_mac:
DEBUGF 1,"Reading MAC"
 
mov edx, [device.io_addr]
add dx, 6
@@:
dec dx
dec dx
in ax, dx
push ax
DEBUGF 1,"."
cmp edx, [device.io_addr]
ja @r
 
DEBUGF 1," %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
 
lea edi, [device.mac]
pop ax
stosw
pop ax
stosw
pop ax
stosw
 
ret
 
align 4
switch_to_wio:
 
DEBUGF 1,"Switching to 16-bit mode\n"
 
mov [device.read_csr], wio_read_csr
mov [device.write_csr], wio_write_csr
mov [device.read_bcr], wio_read_bcr
mov [device.write_bcr], wio_write_bcr
mov [device.read_rap], wio_read_rap
mov [device.write_rap], wio_write_rap
mov [device.sw_reset], wio_reset
 
ret
 
align 4
switch_to_dwio:
 
DEBUGF 1,"Switching to 32-bit mode\n"
 
mov [device.read_csr], dwio_read_csr
mov [device.write_csr], dwio_write_csr
mov [device.read_bcr], dwio_read_bcr
mov [device.write_bcr], dwio_write_bcr
mov [device.read_rap], dwio_read_rap
mov [device.write_rap], dwio_write_rap
mov [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, [device.phy]
and ax, 0x1f
shl ax, 5
or ax, cx
 
mov ecx, BCR_MIIADDR
call [device.write_bcr]
 
mov ecx, BCR_MIIDATA
call [device.read_bcr]
 
ret
 
 
align 4
mdio_write:
 
push eax
and ecx, 0x1f
mov ax, [device.phy]
and ax, 0x1f
shl ax, 5
or ax, cx
 
mov ecx, BCR_MIIADDR
call [device.write_bcr]
 
pop eax
mov ecx, BCR_MIIDATA
call [device.write_bcr]
 
ret
 
 
align 4
check_media:
 
DEBUGF 1, "check_media\n"
 
test [device.mii], 1
jnz mii_link_ok
 
mov ecx, BCR_LED0
call [device.read_bcr]
cmp eax, 0xc0
 
DEBUGF 1, "link status=0x%x\n", ax
 
ret
 
 
 
; End of code
 
align 4 ; Place all initialised data here
 
devices dd 0
version dd (5 shl 16) or (API_VERSION and 0xFFFF)
my_service db 'PCnet',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
 
section '.data' data readable writable align 16 ; place all uninitialized data place here
 
device_list rd MAX_DEVICES ; This list contains all pointers to device structures the driver is handling
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
/drivers/ethernet/rhine.asm
0,0 → 1,1679
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2010-2013. 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 MS COFF
 
API_VERSION = 0x01000100
DRIVER_VERSION = 5
 
MAX_DEVICES = 16
 
DEBUG = 1
__DEBUG__ = 1
__DEBUG_LEVEL__ = 2
 
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
RX_BUF_LEN = (8192 shl RX_BUF_LEN_IDX)
 
; 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 xfer.
RX_DMA_BURST = 4 ; Maximum PCI burst, '4' is 256 bytes
TX_DMA_BURST = 4
 
 
include '../proc32.inc'
include '../imports.inc'
include '../fdo.inc'
include '../netdrv.inc'
 
public START
public service_proc
public version
 
 
;**************************************************************************
; 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 programed status, 73h means programed
EECSR_EMBP = 0x40 ; eeprom embeded 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 embeded 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)
 
 
virtual at ebx
 
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-(($ - device) and 0xff) ; align 256
.tx_ring rb tx_head.sizeof*TX_RING_SIZE
 
rb 0x100-(($ - device) and 0xff) ; align 256
.rx_ring rb rx_head.sizeof*RX_RING_SIZE
 
.size = $ - device
 
end virtual
 
virtual at 0
rx_head:
.status dd ?
.control dd ?
.buff_addr dd ? ; address
.next_desc dd ? ;
 
.buff_addr_virt dd ?
rd 3 ; alignment
.sizeof:
end virtual
 
 
virtual at 0
tx_head:
.status dd ?
.control dd ?
.buff_addr dd ? ; address
.next_desc dd ? ;
 
.buff_addr_virt dd ?
rd 3 ; alignment
.sizeof:
end virtual
 
 
 
section '.flat' code readable align 16
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; proc START ;;
;; ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
proc START stdcall, state:dword
 
cmp [state], 1
jne .exit
 
.entry:
 
DEBUGF 2,"Loading %s driver\n", my_service
stdcall RegService, my_service, service_proc
ret
 
.fail:
.exit:
xor eax, eax
ret
 
endp
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; proc SERVICE_PROC ;;
;; ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
proc service_proc stdcall, ioctl:dword
 
mov edx, [ioctl]
mov eax, [IOCTL.io_code]
 
;------------------------------------------------------
 
cmp eax, 0 ;SRV_GETVERSION
jne @F
 
cmp [IOCTL.out_size], 4
jb .fail
mov eax, [IOCTL.output]
mov [eax], dword API_VERSION
 
xor eax, eax
ret
 
;------------------------------------------------------
@@:
cmp eax, 1 ;SRV_HOOK
jne .fail
 
cmp [IOCTL.inp_size], 3 ; Data input must be at least 3 bytes
jb .fail
 
mov eax, [IOCTL.input]
cmp byte [eax], 1 ; 1 means device number and bus number (pci) are given
jne .fail ; other types arent supported for this card yet
 
; check if the device is already listed
 
mov esi, device_list
mov ecx, [devices]
test ecx, ecx
jz .firstdevice
 
; mov eax, [IOCTL.input] ; get the pci bus and device numbers
mov ax , [eax+1] ;
.nextdevice:
mov ebx, [esi]
cmp al, byte[device.pci_bus]
jne @f
cmp ah, byte[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, device.size, .fail ; Allocate the buffer for device structure
 
; Fill in the direct call addresses into the struct
 
mov [device.reset], reset
mov [device.transmit], transmit
mov [device.unload], unload
mov [device.name], my_service
 
; save the pci bus and device numbers
 
mov eax, [IOCTL.input]
movzx ecx, byte[eax+1]
mov [device.pci_bus], ecx
movzx ecx, byte[eax+2]
mov [device.pci_dev], ecx
 
; Now, it's time to find the base io addres of the PCI device
 
PCI_find_io
 
; We've found the io address, find IRQ now
 
PCI_find_irq
 
DEBUGF 1,"Hooking into device, dev:%x, bus:%x, irq:%x, addr:%x\n",\
[device.pci_dev]:1,[device.pci_bus]:1,[device.irq_line]:1,[device.io_addr]:4
 
; Ok, the eth_device structure is ready, let's probe the device
;;; cli
 
call probe ; this function will output in eax
test eax, eax
jnz .err_sti ; If an error occured, exit
 
mov eax, [devices] ; Add the device structure to our device list
mov [device_list+4*eax], ebx ; (IRQ handler uses this list to find device)
inc [devices] ;
 
mov [device.type], NET_TYPE_ETH
call NetRegDev
;;; sti
 
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"
call NetPtrToNum ; This kernel procedure converts a pointer to device struct in ebx
; into a device number in edi
mov eax, edi ; Application wants it in eax instead
DEBUGF 1,"Kernel says: %u\n", eax
ret
 
; If an error occured, remove all allocated data and exit (returning -1 in eax)
 
.destroy:
; todo: reset device into virgin state
 
.err_sti:
sti
 
.err:
stdcall KernelFree, ebx
 
.fail:
or eax, -1
ret
 
;------------------------------------------------------
endp
 
 
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
;; ;;
;; Actual Hardware dependent code starts here ;;
;; ;;
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
 
 
 
probe:
 
mov eax, [device.io_addr]
DEBUGF 1, "Probing card at 0x%x\n", eax
 
; make the card a bus master
PCI_make_bus_master
 
; get device id
stdcall PciRead16, [device.pci_bus], [device.pci_dev], PCI_DEVICE_ID
mov [device.chip_id], ax
 
; get revision id.
PCI_find_rev
 
movzx eax, [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, [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 0
set_io bySTICKHW
in al, dx
and al, 0xFC
out dx, al
 
; (bits written are cleared?)
; disable force PME-enable
set_io 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 byPwrcsrClr
out dx, al
 
.below_x40:
 
; Reset the chip to erase previous misconfiguration.
set_io 0
set_io byCR0
mov ax, CR_SFRST
out dx, ax
 
; if vt3043 delay after reset
cmp [device.revision], 0x40
jae @f
mov esi, 2000 ; 2000ms
call 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 byEECSR
mov al, 0x20
out dx, al
 
; if vt3065 delay after reset
cmp [device.revision], 0x40
jb .not_vt3065
 
; delay 8ms to let MAC stable
mov esi, 8 ; 8ms
call 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 byCFGA
in al, dx
and al, 0xFE
out dx, al
 
; turn on bit2 in PCI configuration register 0x53 , only for 3065
stdcall PciRead8, [device.pci_bus], [device.pci_dev], PCI_REG_MODE3
or al, MODE3_MIION
stdcall PciWrite8, [device.pci_bus], [device.pci_dev], PCI_REG_MODE3, eax
.not_vt3065:
 
; back off algorithm, disable the right-most 4-bit off CFGD
set_io 0
set_io 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:
DEBUGF 1, "."
mov esi, 1
call Sleep
 
stdcall ReadMII, 1
test eax, 0x0020
jnz .read_done
loop .read_again
DEBUGF 1, "timeout!\n"
.read_done:
DEBUGF 1, " OK\n"
 
if DEBUG
 
set_io 0
set_io 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 0
set_io 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 0
set_io 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 [device.chip_id], 0x3043
jne @f
stdcall WriteMII, 0x17, 1 shl 1, 1
@@:
 
; turn on MII link change
set_io 0
set_io byMIICR
in al, dx
and al, 0x7F
out dx, al
push eax
 
call MIIDelay
 
set_io byMIIAD
mov al, 0x41
out dx, al
 
call MIIDelay
 
pop eax
or al, 0x80
set_io byMIICR
out dx, al
 
;**************************************************************************;
;* ETH_RESET - Reset adapter *;
;**************************************************************************;
 
reset:
 
DEBUGF 1, "reset\n"
 
; attach int handler
 
movzx eax, [device.irq_line]
DEBUGF 2,"Attaching int handler to irq %x\n", eax:1
stdcall AttachIntHandler, eax, int_handler, dword 0
test eax, eax
jnz @f
DEBUGF 2,"\nCould not attach int handler!\n"
; or eax, -1
; ret
@@:
 
; Soft reset the chip.
set_io 0
set_io byCR0
mov ax, CR_SFRST
out dx, ax
 
call MIIDelay
 
; Initialize rings
call init_ring
 
; Setup Multicast
call set_rx_mode
 
; set TCR RCR threshold to store and forward
set_io 0
set_io byBCR0
mov al, 0x3E
out dx, al
 
set_io byBCR1
mov al, 0x38
out dx, al
 
set_io byRCR
mov al, 0x2C
out dx, al
 
set_io byTCR
mov al, 0x60
out dx, al
 
; Set Fulldupex
 
call QueryAuto
test eax, eax ; full duplex?
jz @f
 
set_io 0
set_io byCFGD
mov al, CFGD_CFDX
out dx, al
 
set_io byCR0
mov ax, CR_FDX
out dx, ax
@@:
 
; ENABLE interrupts
set_io 0
set_io byIMR0
mov ax, DEFAULT_INTR
out dx, ax
 
; KICK NIC to WORK
 
set_io 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 [device.mtu], 1514
 
; Set link state to unknown
mov [device.state], ETH_LINK_UNKOWN
 
; say reset was successfull
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 0
set_io byTCR
mov al, 0x61
out dx, al
 
; Stop the chip's Tx and Rx processes.
set_io byCR0
mov ax, CR_STOP
out dx, ax
pop edx eax
 
ret
 
 
 
 
align 4
reload_eeprom:
 
DEBUGF 1, "Reload eeprom\n"
 
set_io 0
set_io 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 1, "Reload 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, [device.rx_ring]
mov eax, edi
GetRealAddr
mov esi, eax
push esi
mov ecx, RX_RING_SIZE
.rx_init:
add esi, rx_head.sizeof
mov [edi + rx_head.status], RX_SBITS_OWN_BIT
mov [edi + rx_head.control], PKT_BUF_SZ
push ecx
stdcall KernelAlloc, PKT_BUF_SZ
pop ecx
mov [edi + rx_head.buff_addr_virt], eax
GetRealAddr
mov [edi + rx_head.buff_addr], eax ; buffer ptr
mov [edi + rx_head.next_desc], esi ; next head
add edi, rx_head.sizeof
dec ecx
jnz .rx_init
pop [edi - rx_head.sizeof + rx_head.next_desc] ; Mark the last entry as wrapping the ring.
 
 
lea edi, [device.tx_ring]
mov eax, edi
GetRealAddr
mov esi, eax
push esi
mov ecx, TX_RING_SIZE
.tx_init:
add esi, tx_head.sizeof
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, tx_head.sizeof
dec ecx
jnz .tx_init
pop [edi - tx_head.sizeof + tx_head.next_desc] ; Mark the last entry as wrapping the ring.
 
; write Descriptors to MAC
lea eax, [device.rx_ring]
GetRealAddr
set_io 0
set_io dwCurrentRxDescAddr
out dx, eax
 
lea eax, [device.tx_ring]
GetRealAddr
set_io dwCurrentTxDescAddr
out dx, eax
 
xor eax, eax
mov [device.cur_rx], ax
mov [device.cur_tx], ax
mov [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 0
set_io byMIIAD
in al, dx
mov bl, al
 
set_io byMIICR
in al, dx
mov bh, al
and al, 0x7F
out dx, al
 
call MIIDelay
 
mov al, byte [byMIIIndex]
set_io byMIIAD
out dx, al
 
call MIIDelay
 
set_io 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
call Sleep
dec ecx
jnz .read_again
DEBUGF 1, "\nReadMII timeout!\n"
@@:
 
call MIIDelay
 
set_io byMIIAD
in ax, dx
 
push eax
mov ax, bx
set_io byMIIAD
out dx, al
 
shr ax, 8
set_io byMIICR
out dx, al
 
call MIIDelay
 
pop eax
and eax, 0xFFFF
rol ax, 8 ;;;;; I dont know how or why but it seems needed...
 
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 0
set_io byMIIAD
in al, dx
mov bl, al
 
set_io byMIICR
in al, dx
mov bh, al
and al, 0x7F
out dx, al
 
call MIIDelay
 
mov al, byte [byMIISetByte]
set_io byMIIAD
out dx, al
 
call MIIDelay
 
set_io 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
call Sleep
dec ecx
jnz .read_again0
DEBUGF 1, "WriteMII timeout 1\n"
.done:
 
call MIIDelay
 
set_io wMIIDATA
in ax, dx
 
mov ecx, [byMIISetBit]
rol cx, 8 ;;;;;;;;;;;;;;;;; CHECKME
 
cmp byte [byMIIOP], 0
jne @f
not ecx
and ax, cx
jmp .end_mascarad
@@:
or ax, cx
.end_mascarad:
 
set_io wMIIDATA
out dx, ax
 
call MIIDelay
 
set_io 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
call Sleep
dec ecx
jnz .read_again1
DEBUGF 1, "WriteMII timeout 2\n"
@@:
 
call MIIDelay
 
mov ax, bx
and al, 0x7F
set_io byMIIAD
out dx, al
 
shr ax, 8
set_io 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 0
set_io byMAR0
out dx, eax
 
set_io byMAR4
out dx, eax
 
set_io 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 StationAddr
in al, dx
}
 
 
 
align 4
read_mac:
 
DEBUGF 1, "Ethernet Address: "
 
lea edi, [device.mac]
set_io 0
set_io byPAR0
mov ecx, 6
.next:
in al, dx
stosb
DEBUGF 1, "-%x", al
inc edx
dec ecx
jnz .next
DEBUGF 1, "\n"
 
ret
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Transmit ;;
;; ;;
;; In: buffer pointer in [esp+4] ;;
;; size of buffer in [esp+8] ;;
;; pointer to device structure in ebx ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
align 4
transmit:
DEBUGF 2,"\nTransmitting packet, buffer:%x, size:%u\n", [esp+4], [esp+8]
mov eax, [esp+4]
DEBUGF 2,"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 dword [esp+8], 1514
ja .fail
cmp dword [esp+8], 60
jb .fail
 
movzx eax, [device.cur_tx]
mov ecx, tx_head.sizeof
mul ecx
lea edi, [device.tx_ring]
add edi, eax
 
cmp [edi + tx_head.buff_addr_virt], 0
jne .fail
 
mov eax, [esp+4]
mov [edi + tx_head.buff_addr_virt], eax
GetRealAddr
mov [edi + tx_head.buff_addr], eax
mov ecx, [esp+8]
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 0
set_io byCR1
in al, dx
or al, CR1_TDMD1
out dx, al
 
inc [device.cur_tx]
and [device.cur_tx], TX_RING_SIZE-1
 
;outw(IMRShadow,byIMR0); ;
 
; Update stats
inc [device.packets_tx]
mov ecx, [esp+8] ;;;;;
add dword [device.bytes_tx], ecx
adc dword [device.bytes_tx + 4], 0
 
ret 8
 
.fail:
DEBUGF 1, "Failed!\n"
 
ret 8
 
 
 
 
 
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Interrupt handler ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
int_handler:
 
push ebx esi edi
 
DEBUGF 1,"\n%s int ", my_service
 
; Find pointer of device wich made IRQ occur
 
mov ecx, [devices]
test ecx, ecx
jz .nothing
mov esi, device_list
.nextdevice:
mov ebx, [esi]
 
set_io 0
set_io 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 descripter pointer
 
movzx eax, [device.cur_rx]
mov ecx, rx_head.sizeof
mul ecx
lea edi, [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 [device.bytes_rx], ecx
adc dword [device.bytes_rx + 4], 0
inc [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
stdcall KernelAlloc, PKT_BUF_SZ
pop edi
mov [edi + rx_head.buff_addr_virt], eax
GetRealAddr
mov [edi + rx_head.buff_addr], eax
mov [edi + rx_head.status], RX_SBITS_OWN_BIT
 
; Use next descriptor next time
 
inc [device.cur_rx]
and [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, [device.last_tx]
mov ecx, tx_head.sizeof
mul ecx
lea edi, [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
call KernelFree
 
inc [device.last_tx]
and [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 [device.chip_id], 0x3065 ;if (tp->chip_id == 0x3065)
jne @f
push ax
xor eax, eax
set_io 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 0
set_io IntrStatus2
push ax
mov al, 0x08
out dx, al
pop ax
@@:
set_io 0
set_io IntrStatus
out dx, ax
IOSYNC
 
end if
 
pop edi esi ebx
xor eax, eax
inc eax
 
ret
 
 
 
 
 
; End of code
 
section '.data' data readable writable align 16 ; place all uninitialized data here
align 4 ; Place all initialised data here
 
devices dd 0
version dd (DRIVER_VERSION shl 16) or (API_VERSION and 0xFFFF)
my_service db 'RHINE',0 ; max 16 chars including zero
 
devicelist:
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 wich FDO uses will be included here
 
device_list rd MAX_DEVICES ; This list contains all pointers to device structures the driver is handling
 
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
/drivers/ethernet/sis900.asm
0,0 → 1,1222
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. 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 MS COFF
 
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
TX_BUFF_SZ = 1516 ; Buffer size for each Tx buffer
MAX_ETH_FRAME_SIZE = 1516
 
API_VERSION = 0x01000100
DRIVER_VERSION = 5
 
MAX_DEVICES = 16
 
DEBUG = 1
__DEBUG__ = 1
__DEBUG_LEVEL__ = 2
 
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
 
 
include '../proc32.inc'
include '../imports.inc'
include '../fdo.inc'
include '../netdrv.inc'
 
public START
public version
 
 
virtual at ebx
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 2 ; alignment
 
.txd rd (4 * NUM_TX_DESC)
.rxd rd (4 * NUM_RX_DESC)
 
.size = $ - device
 
end virtual
 
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
}
 
 
section '.flat' code readable align 16
 
; Driver entry point - register our service when the driver is loading.
; TODO: add needed operations when unloading
START:
cmp dword [esp+4], 1
jne .exit
stdcall RegService, my_service, service_proc
ret 4
.exit:
xor eax, eax
ret 4
 
; 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, [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 [IOCTL.out_size], 4
jb .fail
; 3b. Write result to the output buffer.
mov eax, [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 [IOCTL.inp_size], 3
jb .fail
; 4b. First byte of input is bus type, 1 stands for PCI.
mov eax, [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, [IOCTL.input] ; get the pci bus and device numbers
mov ax, [eax+1] ;
.nextdevice:
mov ebx, [esi]
cmp al, byte[device.pci_bus]
jne @f
cmp ah, byte[device.pci_dev]
je .find_devicenum ; Device is already loaded, let's find it's 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, device.size, .fail
; 4i. Save PCI coordinates
mov eax, [IOCTL.input]
movzx ecx, byte[eax+1]
mov [device.pci_bus], ecx
movzx ecx, byte[eax+2]
mov [device.pci_dev], ecx
; 4j. Fill in the direct call addresses into the struct.
mov [device.reset], reset
mov [device.transmit], transmit
mov [device.unload], unload
mov [device.name], my_service
 
; 4k. Now, it's time to find the base io addres of the PCI device
; TODO: implement check if bus and dev exist on this machine
 
; Now, it's time to find the base io addres of the PCI device
PCI_find_io
 
; We've found the io address, find IRQ now
PCI_find_irq
 
; 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 [device.type], NET_TYPE_ETH
call 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:
call 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:
stdcall 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 CHECKME
stdcall PciWrite8, [device.pci_bus], [device.pci_dev], 0x40, 0
 
PCI_make_bus_master
 
PCI_adjust_latency 64
 
; Get Card Revision
stdcall PciRead8, [device.pci_bus], [device.pci_dev], 0x08
mov [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 [device.table_entries], 16
cmp [device.pci_revision], SIS635A_900_REV
jae @f
cmp [device.pci_revision], SIS900B_900_REV
je @f
mov [device.table_entries], 8
@@:
 
; TODO: Probe for mii transceiver
 
jmp reset
 
.notsupported:
DEBUGF 1, "Device not supported\n"
or eax, -1
 
ret
 
reset:
 
DEBUGF 1, "reset\n"
 
movzx eax, [device.irq_line]
stdcall AttachIntHandler, eax, int_handler, 0
 
;--------------------------------------------
; Disable Interrupts and reset Receive Filter
 
set_io 0
set_io ier
xor eax, eax
out dx, eax
 
set_io imr
out dx, eax
 
set_io rfcr
out dx, eax
 
;-----------
; Reset Card
 
set_io 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 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 cfg
mov eax, PESEL ; Configuration Register Bit
cmp [device.pci_revision], SIS635A_900_REV
je .match
cmp [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 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 0
set_io rfcr
shl eax, 16 ; high word of eax tells card which mac byte to write
out dx, eax ;
set_io rfdr
mov ax, word [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 rfcr
or eax, RFEN ; enable filtering
out dx, eax ; set register
 
DEBUGF 1, "Initialising TX Descriptors\n"
 
mov ecx, NUM_TX_DESC
lea esi, [device.txd]
.txdescloop:
lea eax, [esi + 16] ; next ptr
GetRealAddr
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, [device.txd]
GetRealAddr
mov dword [esi - 16], eax ; correct last descriptor link ptr
 
set_io txdp ; TX Descriptor Pointer
; lea eax, [device.txd]
; GetRealAddr
out dx, eax
 
mov [device.cur_tx], 0 ; Set current tx descriptor to 0
mov [device.last_tx], 0
 
DEBUGF 1, "Initialising RX Descriptors\n"
 
mov ecx, NUM_RX_DESC
lea esi, [device.rxd]
.rxdescloop:
lea eax, [esi + 16] ; next ptr
GetRealAddr
mov dword [esi], eax
mov dword [esi + 4], RX_BUFF_SZ ; size
 
push ecx esi
stdcall KernelAlloc, RX_BUFF_SZ
pop esi ecx
test eax, eax
jz .fail
mov dword [esi + 12], eax ; address
GetRealAddr
mov dword [esi + 8], eax ; real address
add esi, 16
dec ecx
jnz .rxdescloop
 
lea eax, [device.rxd]
GetRealAddr
mov dword [esi - 16], eax ; correct last descriptor link ptr
 
set_io 0
set_io rxdp
; lea eax, [device.rxd]
; GetRealAddr
out dx, eax
 
mov [device.cur_rx], 0 ; Set current rx descriptor to 0
 
DEBUGF 1, "setting RX mode\n"
 
xor cl, cl
.rxfilterloop:
set_io 0
set_io 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 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, [device.table_entries]
jb .rxfilterloop
 
set_io rfcr ; Receive Filter Control Register offset
mov eax, RFAAB + RFAAM + RFAAP + RFEN
out dx, eax
 
set_io 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 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 imr
mov eax, IE ; Interrupt enable mask
out dx, eax
 
set_io cr
in eax, dx
or eax, RxENA ; Enable Receive
out dx, eax
 
set_io ier ; Interrupt enable
mov eax, 1
out dx, eax
 
mov [device.mtu], 1514
 
; Set link state to unknown
mov [device.state], ETH_LINK_UNKOWN
 
xor eax, eax
ret
 
.fail:
DEBUGF 1, "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: "
 
;-------------------------------
; Send Request for eeprom access
 
set_io 0
set_io 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 1, "Access to EEprom failed!\n", 0
 
set_io 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 [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 2,"%x-%x-%x-%x-%x-%x\n",[device.mac]:2,[device.mac+1]:2,[device.mac+2]:2,[device.mac+3]:2,[device.mac+4]:2,[device.mac+5]:2
 
;-------------------------------------
; Tell EEPROM We are Done Accessing It
 
.done:
set_io 0
set_io 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: "
 
;------------------------------------
; 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 [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 2,"%x-%x-%x-%x-%x-%x\n",[device.mac]:2,[device.mac+1]:2,[device.mac+2]:2,[device.mac+3]:2,[device.mac+4]:2,[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: "
 
set_io 0
set_io rfcr
in eax, dx
mov esi, eax
 
set_io cr
or eax, RELOAD
out dx, eax
 
xor eax, eax
out dx, eax
 
;-----------------------------------------------
; Disable packet filtering before setting filter
 
set_io rfcr
mov eax, esi
and eax, not RFEN
out dx, eax
 
;---------------------------------
; Load MAC to filter data register
 
xor ecx, ecx
lea edi, [device.mac]
.loop:
set_io 0
set_io rfcr
mov eax, ecx
shl eax, RFADDR_shift
out dx, eax
 
set_io rfdr
in ax, dx
stosw
inc ecx
cmp ecx, 3
jb .loop
 
;------------------------
; Enable packet filtering
 
set_io rfcr
mov eax, esi
or eax, RFEN
out dx, eax
 
DEBUGF 2,"%x-%x-%x-%x-%x-%x\n",[device.mac]:2,[device.mac+1]:2,[device.mac+2]:2,[device.mac+3]:2,[device.mac+4]:2,[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 0
set_io 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 1,'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
transmit:
DEBUGF 1,"Transmitting packet, buffer:%x, size:%u\n",[esp+4],[esp+8]
mov eax, [esp+4]
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 dword [esp + 8], MAX_ETH_FRAME_SIZE
ja .error
cmp dword [esp + 8], 60
jb .error
 
movzx ecx, [device.cur_tx]
shl ecx, 4 ; *16
lea ecx, [device.txd + ecx]
 
test dword [ecx + 4], 0x80000000 ; card owns descriptor ?
jnz .error
 
mov eax, [esp + 4]
mov dword [ecx + 12], eax
GetRealAddr
mov dword [ecx + 8], eax ; buffer address
 
mov eax, [esp + 8]
and eax, DSIZE
or eax, 0x80000000 ; card owns descriptor
mov dword [ecx + 4], eax ; status field
 
set_io 0
set_io cr
in eax, dx
or eax, TxENA ; Enable the transmit state machine
out dx, eax
 
inc [device.cur_tx]
and [device.cur_tx], NUM_TX_DESC-1
 
; update stats
mov ecx, [esp + 8]
inc [device.packets_tx]
add dword [device.bytes_tx], ecx
adc dword [device.bytes_tx + 4], 0
 
.finish:
DEBUGF 1,"Packet sent!\n"
xor eax, eax
ret 8
 
.error:
DEBUGF 1,"ERROR!\n"
stdcall KernelFree, [esp+4]
or eax, -1
ret 8
 
 
;***************************************************************************
;
; 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,"\n%s int\n", my_service
 
; 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 0
set_io 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 ", ebx, ax
 
test ax, RxOK
jz .no_rx_
 
push ax
 
.rx_loop:
 
;-----------
; Get Status
movzx eax, [device.cur_rx] ; find current descriptor
shl eax, 4 ; * 16
mov ecx, dword[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 [device.packets_rx]
add dword [device.bytes_rx], ecx
adc dword [device.bytes_rx + 4], 0
 
push ebx
push .return
push ecx ; packet size
pushd [device.rxd + eax + 12] ; packet ptr
DEBUGF 1, "Packet received OK\n"
jmp Eth_input
.return:
pop ebx
 
; Reset status, allow ethernet card access to descriptor
stdcall KernelAlloc, RX_BUFF_SZ
test eax, eax
jz .fail
movzx ecx, [device.cur_rx]
shl ecx, 4 ; *16
lea ecx, [device.rxd + ecx]
mov dword [ecx + 12], eax
GetRealAddr
mov dword [ecx + 8], eax
mov dword [ecx + 4], RX_BUFF_SZ
 
inc [device.cur_rx] ; get next descriptor
and [device.cur_rx], NUM_RX_DESC-1 ; only 4 descriptors 0-3
 
jmp .rx_loop
 
.no_rx:
set_io 0
set_io 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, [device.last_tx]
shl ecx, 4 ; *16
lea ecx, [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
call KernelFree
 
inc [device.last_tx]
and [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 1, "Packet error: %x\n", ecx
jmp .fail
 
.error_size:
DEBUGF 1, "Packet too large/small\n"
jmp .fail
 
 
 
 
 
; End of code
 
align 4 ; Place all initialised data here
 
devices dd 0
 
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
 
version dd (DRIVER_VERSION shl 16) or (API_VERSION and 0xFFFF)
my_service db 'SIS900',0 ; max 16 chars include zero
 
include_debug_strings ; All data wich FDO uses will be included here
 
section '.data' data readable writable align 16; place all uninitialized data place here
 
device_list rd MAX_DEVICES ; This list contains all pointers to device structures the driver is handling
 
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property