/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 |
/drivers/fdo.inc |
---|
0,0 → 1,432 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2004-2007. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
; |
; Formatted Debug Output (FDO) |
; Copyright (c) 2005-2006, mike.dld |
; Created: 2005-01-29, Changed: 2006-11-10 |
; |
; For questions and bug reports, mail to mike.dld@gmail.com |
; |
; Available format specifiers are: %s, %d, %u, %x (with partial width support) |
; |
; to be defined: |
; __DEBUG__ equ 1 |
; __DEBUG_LEVEL__ equ 5 |
macro debug_func name { |
if used name |
name@of@func equ name |
} |
macro debug_beginf { |
align 4 |
name@of@func: |
} |
debug_endf fix end if |
macro DEBUGS _sign,[_str] { |
common |
local tp |
tp equ 0 |
match _arg:_num,_str \{ |
DEBUGS_N _sign,_num,_arg |
tp equ 1 |
\} |
match =0 _arg,tp _str \{ |
DEBUGS_N _sign,,_arg |
\} |
} |
macro DEBUGS_N _sign,_num,[_str] { |
common |
pushf |
pushad |
local ..str,..label,is_str |
is_str = 0 |
forward |
if _str eqtype '' |
is_str = 1 |
end if |
common |
if is_str = 1 |
jmp ..label |
..str db _str,0 |
..label: |
add esp,4*8+4 |
mov edx,..str |
sub esp,4*8+4 |
else |
mov edx,_str |
end if |
if ~_num eq |
if _num eqtype eax |
if _num in <eax,ebx,ecx,edx,edi,ebp,esp> |
mov esi,_num |
else if ~_num eq esi |
movzx esi,_num |
end if |
else if _num eqtype 0 |
mov esi,_num |
else |
local tp |
tp equ 0 |
match [_arg],_num \{ |
mov esi,dword[_arg] |
tp equ 1 |
\} |
match =0 =dword[_arg],tp _num \{ |
mov esi,dword[_arg] |
tp equ 1 |
\} |
match =0 =word[_arg],tp _num \{ |
movzx esi,word[_arg] |
tp equ 1 |
\} |
match =0 =byte[_arg],tp _num \{ |
movzx esi,byte[_arg] |
tp equ 1 |
\} |
match =0,tp \{ |
'Error: specified string width is incorrect' |
\} |
end if |
else |
mov esi,0x7FFFFFFF |
end if |
call fdo_debug_outstr |
popad |
popf |
} |
macro DEBUGD _sign,_dec { |
local tp |
tp equ 0 |
match _arg:_num,_dec \{ |
DEBUGD_N _sign,_num,_arg |
tp equ 1 |
\} |
match =0 _arg,tp _dec \{ |
DEBUGD_N _sign,,_arg |
\} |
} |
macro DEBUGD_N _sign,_num,_dec { |
pushf |
pushad |
if (~_num eq) |
if (_dec eqtype eax | _dec eqtype 0) |
'Error: precision allowed only for in-memory variables' |
end if |
if (~_num in <1,2,4>) |
if _sign |
'Error: 1, 2 and 4 are only allowed for precision in %d' |
else |
'Error: 1, 2 and 4 are only allowed for precision in %u' |
end if |
end if |
end if |
if _dec eqtype eax |
if _dec in <ebx,ecx,edx,esi,edi,ebp,esp> |
mov eax,_dec |
else if ~_dec eq eax |
if _sign = 1 |
movsx eax,_dec |
else |
movzx eax,_dec |
end if |
end if |
else if _dec eqtype 0 |
mov eax,_dec |
else |
add esp,4*8+4 |
if _num eq |
mov eax,dword _dec |
else if _num = 1 |
if _sign = 1 |
movsx eax,byte _dec |
else |
movzx eax,byte _dec |
end if |
else if _num = 2 |
if _sign = 1 |
movsx eax,word _dec |
else |
movzx eax,word _dec |
end if |
else |
mov eax,dword _dec |
end if |
sub esp,4*8+4 |
end if |
mov cl,_sign |
call fdo_debug_outdec |
popad |
popf |
} |
macro DEBUGH _sign,_hex { |
local tp |
tp equ 0 |
match _arg:_num,_hex \{ |
DEBUGH_N _sign,_num,_arg |
tp equ 1 |
\} |
match =0 _arg,tp _hex \{ |
DEBUGH_N _sign,,_arg |
\} |
} |
macro DEBUGH_N _sign,_num,_hex { |
pushf |
pushad |
if (~_num eq) & (~_num in <1,2,3,4,5,6,7,8>) |
'Error: 1..8 are only allowed for precision in %x' |
end if |
if _hex eqtype eax |
if _hex in <eax,ebx,ecx,edx,esi,edi,ebp,esp> |
if ~_hex eq eax |
mov eax,_hex |
end if |
mov edx,8 |
else if _hex in <ax,bx,cx,dx,si,di,bp,sp> |
if ~_hex eq ax |
movzx eax,_hex |
end if |
if (_num eq) |
mov edx,4 |
end if |
else if _hex in <al,ah,bl,bh,cl,ch,dl,dh> |
if ~_hex eq al |
movzx eax,_hex |
end if |
if (_num eq) |
mov edx,2 |
end if |
end if |
else if _hex eqtype 0 |
mov eax,_hex |
else |
add esp,4*8+4 |
mov eax,dword _hex |
sub esp,4*8+4 |
end if |
if ~_num eq |
mov edx,_num |
else |
if ~_hex eqtype eax |
mov edx,8 |
end if |
end if |
call fdo_debug_outhex |
popad |
popf |
} |
;----------------------------------------------------------------------------- |
debug_func fdo_debug_outchar |
debug_beginf |
pushad |
movzx ebx,al |
mov eax,1 |
; mov ecx,sys_msg_board |
; call ecx ; sys_msg_board |
stdcall SysMsgBoardChar |
popad |
ret |
debug_endf |
debug_func fdo_debug_outstr |
debug_beginf |
mov eax,1 |
.l1: dec esi |
js .l2 |
movzx ebx,byte[edx] |
or bl,bl |
jz .l2 |
; mov ecx,sys_msg_board |
; call ecx ; sys_msg_board |
stdcall SysMsgBoardChar |
inc edx |
jmp .l1 |
.l2: ret |
debug_endf |
debug_func fdo_debug_outdec |
debug_beginf |
or cl,cl |
jz @f |
or eax,eax |
jns @f |
neg eax |
push eax |
mov al,'-' |
call fdo_debug_outchar |
pop eax |
@@: push 10 |
pop ecx |
push -'0' |
.l1: xor edx,edx |
div ecx |
push edx |
test eax,eax |
jnz .l1 |
.l2: pop eax |
add al,'0' |
jz .l3 |
call fdo_debug_outchar |
jmp .l2 |
.l3: ret |
debug_endf |
debug_func fdo_debug_outhex |
__fdo_hexdigits db '0123456789ABCDEF' |
debug_beginf |
mov cl,dl |
neg cl |
add cl,8 |
shl cl,2 |
rol eax,cl |
.l1: rol eax,4 |
push eax |
and eax,0x0000000F |
mov al,[__fdo_hexdigits+eax] |
call fdo_debug_outchar |
pop eax |
dec edx |
jnz .l1 |
ret |
debug_endf |
;----------------------------------------------------------------------------- |
macro DEBUGF _level,_format,[_arg] { |
common |
if __DEBUG__ = 1 & _level >= __DEBUG_LEVEL__ |
local ..f1,f2,a1,a2,c1,c2,c3,..lbl |
_debug_str_ equ __debug_str_ # a1 |
a1 = 0 |
c2 = 0 |
c3 = 0 |
f2 = 0 |
repeat ..lbl-..f1 |
virtual at 0 |
db _format,0,0 |
load c1 word from %-1 |
end virtual |
if c1 = '%s' |
virtual at 0 |
db _format,0,0 |
store word 0 at %-1 |
load c1 from f2-c2 |
end virtual |
if c1 <> 0 |
DEBUGS 0,_debug_str_+f2-c2 |
end if |
c2 = c2 + 1 |
f2 = %+1 |
DEBUGF_HELPER S,a1,0,_arg |
else if c1 = '%x' |
virtual at 0 |
db _format,0,0 |
store word 0 at %-1 |
load c1 from f2-c2 |
end virtual |
if c1 <> 0 |
DEBUGS 0,_debug_str_+f2-c2 |
end if |
c2 = c2 + 1 |
f2 = %+1 |
DEBUGF_HELPER H,a1,0,_arg |
else if c1 = '%d' | c1 = '%u' |
local c4 |
if c1 = '%d' |
c4 = 1 |
else |
c4 = 0 |
end if |
virtual at 0 |
db _format,0,0 |
store word 0 at %-1 |
load c1 from f2-c2 |
end virtual |
if c1 <> 0 |
DEBUGS 0,_debug_str_+f2-c2 |
end if |
c2 = c2 + 1 |
f2 = %+1 |
DEBUGF_HELPER D,a1,c4,_arg |
else if c1 = '\n' |
c3 = c3 + 1 |
end if |
end repeat |
virtual at 0 |
db _format,0,0 |
load c1 from f2-c2 |
end virtual |
if (c1<>0)&(f2<>..lbl-..f1-1) |
DEBUGS 0,_debug_str_+f2-c2 |
end if |
virtual at 0 |
..f1 db _format,0 |
..lbl: |
__debug_strings equ __debug_strings,_debug_str_,<_format>,..lbl-..f1-1-c2-c3 |
end virtual |
end if |
} |
macro __include_debug_strings dummy,[_id,_fmt,_len] { |
common |
local c1,a1,a2 |
forward |
if defined _len & ~_len eq |
_id: |
a1 = 0 |
a2 = 0 |
repeat _len |
virtual at 0 |
db _fmt,0,0 |
load c1 word from %+a2-1 |
end virtual |
if (c1='%s')|(c1='%x')|(c1='%d')|(c1='%u') |
db 0 |
a2 = a2 + 1 |
else if (c1='\n') |
dw $0A0D |
a1 = a1 + 1 |
a2 = a2 + 1 |
else |
db c1 and 0x0FF |
end if |
end repeat |
db 0 |
end if |
} |
macro DEBUGF_HELPER _letter,_num,_sign,[_arg] { |
common |
local num |
num = 0 |
forward |
if num = _num |
DEBUG#_letter _sign,_arg |
end if |
num = num+1 |
common |
_num = _num+1 |
} |
macro include_debug_strings { |
if __DEBUG__ = 1 |
match dbg_str,__debug_strings \{ |
__include_debug_strings dbg_str |
\} |
end if |
} |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |
/drivers/imports.inc |
---|
1,151 → 1,102 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2004-2007. All rights reserved. ;; |
;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
; all exported kernel functions and data |
if used RegService |
extrn RegService |
macro kernel_export [name]{ |
forward |
if used name |
if DEBUG |
display 'uses: ',`name,#13,#10 |
end if |
if used GetService |
extrn GetService |
extrn name |
end if |
if used ServiceHandler |
extrn ServiceHandler |
end if |
if used AttachIntHandler |
extrn AttachIntHandler |
end if |
if used FpuSave |
extrn FpuSave |
end if |
if used FpuRestore |
extrn FpuRestore |
end if |
} |
; all exported kernel functions and data |
if used PciApi |
extrn PciApi |
end if |
if used PciRead32 |
extrn PciRead32 |
end if |
if used PciRead8 |
extrn PciRead8 |
end if |
if used PciWrite8 |
extrn PciWrite8 |
end if |
if used AllocPage |
extrn AllocPage |
end if |
if used AllocPages |
extrn AllocPages |
end if |
if used FreePage |
extrn FreePage |
end if |
if used MapPage |
extrn MapPage |
end if |
if used MapSpace |
extrn MapSpace |
end if |
if used GetPgAddr |
extrn GetPgAddr |
end if |
if used CommitPages |
extrn CommitPages |
end if |
if used ReleasePages |
extrn ReleasePages |
end if |
if used AllocKernelSpace |
extrn AllocKernelSpace |
end if |
if used FreeKernelSpace |
extrn FreeKernelSpace |
end if |
if used KernelAlloc |
extrn KernelAlloc |
end if |
if used KernelFree |
extrn KernelFree |
end if |
if used UserAlloc |
extrn UserAlloc |
end if |
if used UserFree |
extrn UserFree |
end if |
if used Kmalloc |
extrn Kmalloc |
end if |
if used Kfree |
extrn Kfree |
end if |
if used CreateObject |
extrn CreateObject |
end if |
if used DestroyObject |
extrn DestroyObject |
end if |
if used CreateEvent |
extrn CreateEvent |
end if |
if used RaiseEvent |
extrn RaiseEvent |
end if |
if used WaitEvent |
extrn WaitEvent |
end if |
if used DestroyEvent |
extrn DestroyEvent |
end if |
if used ClearEvent |
extrn ClearEvent |
end if |
if used LoadCursor |
extrn LoadCursor |
end if |
if used SetHwCursor |
extrn SetHwCursor |
end if |
if used HwCursorRestore |
extrn HwCursorRestore |
end if |
if used HwCursorCreate |
extrn HwCursorCreate |
end if |
if used SysMsgBoardStr |
extrn SysMsgBoardStr |
end if |
if used GetCurrentTask |
extrn GetCurrentTask |
end if |
if used LoadFile |
extrn LoadFile |
end if |
if used SendEvent |
extrn SendEvent |
end if |
if used SetMouseData |
extrn SetMouseData |
end if |
if used Sleep |
extrn Sleep |
end if |
if used GetTimerTicks |
extrn GetTimerTicks |
end if |
if used LFBAddress |
extrn LFBAddress |
end if |
kernel_export \ |
RegService,\ |
GetService,\ |
ServiceHandler,\ |
AttachIntHandler,\ |
GetIntHandler,\ |
FpuSave,\ |
FpuRestore,\ |
ReservePortArea,\ |
Boot_Log,\ |
\ |
MutexInit,\ |
MutexLock,\ |
MutexUnlock,\ |
\ |
PciApi,\ |
PciRead32,\ |
PciRead16,\ |
PciRead8,\ |
PciWrite8,\ |
PciWrite16,\ |
PciWrite32,\ |
\ |
AllocPage,\ |
AllocPages,\ |
FreePage,\ |
MapPage,\ |
MapSpace,\ |
MapIoMem,\ |
GetPgAddr,\ |
CommitPages,\ |
ReleasePages,\ |
\ |
AllocKernelSpace,\ |
FreeKernelSpace,\ |
KernelAlloc,\ |
KernelFree,\ |
UserAlloc,\ |
UserFree,\ |
Kmalloc,\ |
Kfree,\ |
CreateRingBuffer,\ |
\ |
GetPid,\ |
CreateObject,\ |
DestroyObject,\ |
CreateEvent,\ |
RaiseEvent,\ |
WaitEvent,\ |
DestroyEvent,\ |
ClearEvent,\ |
\ |
LoadCursor,\ |
SelectHwCursor,\ |
SetHwCursor,\ |
HwCursorRestore,\ |
HwCursorCreate,\ |
\ |
SysMsgBoardStr,\ |
SysMsgBoardChar,\ |
GetCurrentTask,\ |
LoadFile,\ |
SendEvent,\ |
SetMouseData,\ |
Sleep,\ |
GetTimerTicks,\ |
\ |
strncat,\ |
strncpy,\ |
strncmp,\ |
strnlen,\ |
strchr,\ |
strrchr,\ |
\ |
LFBAddress,\ |
GetDisplay,\ |
SetScreen,\ |
\ |
NetRegDev,\ |
NetUnRegDev,\ |
NetPtrToNum,\ |
NetLinkChanged,\ |
Eth_input,\ |
IPv4_input |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |
/drivers/mii.inc |
---|
0,0 → 1,160 |
; Generic MII registers. |
MII_BMCR = 0x00 ; Basic mode control register |
MII_BMSR = 0x01 ; Basic mode status register |
MII_PHYSID1 = 0x02 ; PHYS ID 1 |
MII_PHYSID2 = 0x03 ; PHYS ID 2 |
MII_ADVERTISE = 0x04 ; Advertisement control reg |
MII_LPA = 0x05 ; Link partner ability reg |
MII_EXPANSION = 0x06 ; Expansion register |
MII_CTRL1000 = 0x09 ; 1000BASE-T control |
MII_STAT1000 = 0x0a ; 1000BASE-T status |
MII_ESTATUS = 0x0f ; Extended Status |
MII_DCOUNTER = 0x12 ; Disconnect counter |
MII_FCSCOUNTER = 0x13 ; False carrier counter |
MII_NWAYTEST = 0x14 ; N-way auto-neg test reg |
MII_RERRCOUNTER = 0x15 ; Receive error counter |
MII_SREVISION = 0x16 ; Silicon revision |
MII_RESV1 = 0x17 ; Reserved... |
MII_LBRERROR = 0x18 ; Lpback, rx, bypass error |
MII_PHYADDR = 0x19 ; PHY address |
MII_RESV2 = 0x1a ; Reserved... |
MII_TPISTATUS = 0x1b ; TPI status for 10mbps |
MII_NCONFIG = 0x1c ; Network interface config |
; Basic mode control register. |
BMCR_RESV = 0x003f ; Unused... |
BMCR_SPEED1000 = 0x0040 ; MSB of Speed (1000) |
BMCR_CTST = 0x0080 ; Collision test |
BMCR_FULLDPLX = 0x0100 ; Full duplex |
BMCR_ANRESTART = 0x0200 ; Auto negotiation restart |
BMCR_ISOLATE = 0x0400 ; Disconnect DP83840 from MII |
BMCR_PDOWN = 0x0800 ; Powerdown the DP83840 |
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 = 0x00c0 ; Unused... |
BMSR_ESTATEN = 0x0100 ; Extended Status in R15 |
BMSR_100HALF2 = 0x0200 ; Can do 100BASE-T2 HDX |
BMSR_100FULL2 = 0x0400 ; Can do 100BASE-T2 FDX |
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 |
; Advertisement control register. |
ADVERTISE_SLCT = 0x001f ; Selector bits |
ADVERTISE_CSMA = 0x0001 ; Only selector supported |
ADVERTISE_10HALF = 0x0020 ; Try for 10mbps half-duplex |
ADVERTISE_1000XFULL = 0x0020 ; Try for 1000BASE-X full-duplex |
ADVERTISE_10FULL = 0x0040 ; Try for 10mbps full-duplex |
ADVERTISE_1000XHALF = 0x0040 ; Try for 1000BASE-X half-duplex |
ADVERTISE_100HALF = 0x0080 ; Try for 100mbps half-duplex |
ADVERTISE_1000XPAUSE = 0x0080 ; Try for 1000BASE-X pause |
ADVERTISE_100FULL = 0x0100 ; Try for 100mbps full-duplex |
ADVERTISE_1000XPSE_ASYM = 0x0100 ; Try for 1000BASE-X asym pause |
ADVERTISE_100BASE4 = 0x0200 ; Try for 100mbps 4k packets |
ADVERTISE_PAUSE_CAP = 0x0400 ; Try for pause |
ADVERTISE_PAUSE_ASYM = 0x0800 ; Try for asymetric pause |
ADVERTISE_RESV = 0x1000 ; 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) |
; Link partner ability register. |
LPA_SLCT = 0x001f ; Same as advertise selector |
LPA_10HALF = 0x0020 ; Can do 10mbps half-duplex |
LPA_1000XFULL = 0x0020 ; Can do 1000BASE-X full-duplex |
LPA_10FULL = 0x0040 ; Can do 10mbps full-duplex |
LPA_1000XHALF = 0x0040 ; Can do 1000BASE-X half-duplex |
LPA_100HALF = 0x0080 ; Can do 100mbps half-duplex |
LPA_1000XPAUSE = 0x0080 ; Can do 1000BASE-X pause |
LPA_100FULL = 0x0100 ; Can do 100mbps full-duplex |
LPA_1000XPAUSE_ASYM = 0x0100 ; Can do 1000BASE-X pause asym |
LPA_100BASE4 = 0x0200 ; Can do 100mbps 4k packets |
LPA_PAUSE_CAP = 0x0400 ; Can pause |
LPA_PAUSE_ASYM = 0x0800 ; Can pause asymetrically |
LPA_RESV = 0x1000 ; Unused... |
LPA_RFAULT = 0x2000 ; Link partner faulted |
LPA_LPACK = 0x4000 ; Link partner acked us |
LPA_NPAGE = 0x8000 ; Next page bit |
LPA_DUPLEX = (LPA_10FULL or LPA_100FULL) |
LPA_100 = (LPA_100FULL or LPA_100HALF or LPA_100BASE4) |
; Expansion register for auto-negotiation. |
EXPANSION_NWAY = 0x0001 ; Can do N-way auto-nego |
EXPANSION_LCWP = 0x0002 ; Got new RX page code word |
EXPANSION_ENABLENPAGE = 0x0004 ; This enables npage words |
EXPANSION_NPCAPABLE = 0x0008 ; Link partner supports npage |
EXPANSION_MFAULTS = 0x0010 ; Multiple faults detected |
EXPANSION_RESV = 0xffe0 ; Unused... |
ESTATUS_1000_TFULL = 0x2000 ; Can do 1000BT Full |
ESTATUS_1000_THALF = 0x1000 ; Can do 1000BT Half |
; N-way test register. |
NWAYTEST_RESV1 = 0x00ff ; Unused... |
NWAYTEST_LOOPBACK = 0x0100 ; Enable loopback for N-way |
NWAYTEST_RESV2 = 0xfe00 ; Unused... |
; 1000BASE-T Control register |
ADVERTISE_1000FULL = 0x0200 ; Advertise 1000BASE-T full duplex |
ADVERTISE_1000HALF = 0x0100 ; Advertise 1000BASE-T half duplex |
; 1000BASE-T Status register |
LPA_1000LOCALRXOK = 0x2000 ; Link partner local receiver status |
LPA_1000REMRXOK = 0x1000 ; Link partner remote receiver status |
LPA_1000FULL = 0x0800 ; Link partner 1000BASE-T full duplex |
LPA_1000HALF = 0x0400 ; Link partner 1000BASE-T half duplex |
; Flow control flags |
FLOW_CTRL_TX = 0x01 |
FLOW_CTRL_RX = 0x02 |
if used mii_link_ok |
align 4 |
mii_link_ok: |
DEBUGF 1, "mii_link_ok\n" |
; First do a dummy read to latch some MII phys |
mov ecx, MII_BMSR |
call mdio_read |
mov ecx, MII_BMSR |
call mdio_read |
and ax, BMSR_LSTATUS |
DEBUGF 1, "link status=0x%x\n", ax |
ret |
end if |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |
/drivers/netdrv.inc |
---|
0,0 → 1,150 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;; GNU GENERAL PUBLIC LICENSE ;; |
;; Version 2, June 1991 ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
include 'pci.inc' |
include 'mii.inc' |
; Kernel variables |
PAGESIZE = 4096 |
PG_SW = 0x003 |
PG_NOCACHE = 0x018 |
; network driver types |
NET_TYPE_ETH = 1 |
NET_TYPE_SLIP = 2 |
; link state |
ETH_LINK_DOWN = 0 ; Link is down |
ETH_LINK_UNKOWN = 1b ; There could be an active link |
ETH_LINK_FD = 10b ; full duplex flag |
ETH_LINK_10M = 100b ; 10 mbit |
ETH_LINK_100M = 1000b ; 100 mbit |
ETH_LINK_1G = 10000b ; gigabit |
LAST_IO = 0 |
macro set_io addr { |
if addr = 0 |
mov edx, [device.io_addr] |
else if addr = LAST_IO |
else |
add edx, addr - LAST_IO |
end if |
LAST_IO = addr |
} |
macro allocate_and_clear dest, size, err { |
; We need to allocate at least 8 pages, if we want a continuous memory in ram |
push edx |
if (size < 8*4096) & (size > 4096) |
stdcall KernelAlloc, 8*4096 |
else |
stdcall KernelAlloc, size |
end if |
pop edx |
test eax, eax |
jz err |
mov dest, eax ; Save the address to it into the device struct |
mov edi, eax ; look at last part of code! |
; Release the unused pages (if any) |
if (size < 8*4096) & (size > 4096) |
add eax, (size/4096+1)*4096 |
mov ecx, 8-(size/4096+1) |
push edx |
call ReleasePages |
pop edx |
end if |
; Clear the allocated buffer |
mov ecx, size/4 ; divide by 4 because of DWORD |
xor eax, eax |
rep stosd |
} |
struc IOCTL { |
.handle dd ? |
.io_code dd ? |
.input dd ? |
.inp_size dd ? |
.output dd ? |
.out_size dd ? |
} |
virtual at edx |
IOCTL IOCTL |
end virtual |
if used null_op |
align 4 |
null_op: |
or eax, -1 |
ret |
end if |
macro GetRealAddr { ; input and output is eax |
push ax |
call GetPgAddr |
and word[esp], PAGESIZE - 1 |
or ax, word[esp] |
inc esp |
inc esp |
} |
macro NET_DEVICE { |
.type dd ? ; Type field |
.mtu dd ? ; Maximal Transmission Unit |
.name dd ? ; Ptr to 0 terminated string |
.unload dd ? ; Ptrs to driver functions |
.reset dd ? ; |
.transmit dd ? ; |
.bytes_tx dq ? ; Statistics, updated by the driver |
.bytes_rx dq ? ; |
.packets_tx dd ? ; |
.packets_rx dd ? ; |
.state dd ? ; link state (0 = no link) |
.hwacc dd ? ; bitmask stating enabled HW accelerations |
.end: |
} |
macro ETH_DEVICE { |
NET_DEVICE |
.mac dp ? |
dw ? ; qword alignment |
} |
macro SLIP_DEVICE { |
NET_DEVICE |
} |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |
/drivers/pci.inc |
---|
0,0 → 1,132 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;; GNU GENERAL PUBLIC LICENSE ;; |
;; Version 2, June 1991 ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
; PCI Bus defines |
PCI_HEADER_TYPE = 0x0e ; 8 bit |
PCI_BASE_ADDRESS_0 = 0x10 ; 32 bit |
PCI_BASE_ADDRESS_1 = 0x14 ; 32 bits |
PCI_BASE_ADDRESS_2 = 0x18 ; 32 bits |
PCI_BASE_ADDRESS_3 = 0x1c ; 32 bits |
PCI_BASE_ADDRESS_4 = 0x20 ; 32 bits |
PCI_BASE_ADDRESS_5 = 0x24 ; 32 bits |
PCI_BASE_ADDRESS_SPACE_IO = 0x01 |
PCI_BASE_ADDRESS_IO_MASK = 0xFFFFFFFC |
PCI_BASE_ADDRESS_MEM_MASK = 0xFFFFFFF0 |
; PCI programming |
PCI_VENDOR_ID = 0x00 ; 16 bit |
PCI_DEVICE_ID = 0x02 ; 16 bits |
PCI_REG_COMMAND = 0x4 ; command register |
PCI_REG_STATUS = 0x6 ; status register |
PCI_REVISION_ID = 0x08 ; 8 bits |
PCI_REG_LATENCY = 0xd ; latency timer register |
PCI_REG_CAP_PTR = 0x34 ; capabilities pointer |
PCI_REG_IRQ = 0x3c |
PCI_REG_CAPABILITY_ID = 0x0 ; capapility ID in pm register block |
PCI_REG_PM_STATUS = 0x4 ; power management status register |
PCI_REG_PM_CTRL = 0x4 ; power management control register |
PCI_BIT_PIO = 1 ; bit0: io space control |
PCI_BIT_MMIO = 2 ; bit1: memory space control |
PCI_BIT_MASTER = 4 ; bit2: device acts as a PCI master |
macro PCI_find_io { |
local .check, .inc, .got |
xor eax, eax |
mov esi, PCI_BASE_ADDRESS_0 |
.check: |
stdcall PciRead32, [device.pci_bus], [device.pci_dev], esi |
test eax, PCI_BASE_ADDRESS_IO_MASK |
jz .inc |
test eax, PCI_BASE_ADDRESS_SPACE_IO |
jz .inc |
and eax, PCI_BASE_ADDRESS_IO_MASK |
jmp .got |
.inc: |
add esi, 4 |
cmp esi, PCI_BASE_ADDRESS_5 |
jbe .check |
xor eax, eax |
.got: |
mov [device.io_addr], eax |
} |
macro PCI_find_mmio32 { |
local .check, .inc, .got |
mov esi, PCI_BASE_ADDRESS_0 |
.check: |
stdcall PciRead32, [device.pci_bus], [device.pci_dev], esi |
test eax, PCI_BASE_ADDRESS_SPACE_IO ; mmio address? |
jnz .inc |
test eax, 100b ; 64 bit? |
jnz .inc |
and eax, not 1111b |
jmp .got |
.inc: |
add esi, 4 |
cmp esi, PCI_BASE_ADDRESS_5 |
jbe .check |
xor eax, eax |
.got: |
mov [device.mmio_addr], eax |
} |
macro PCI_find_irq { |
stdcall PciRead8, [device.pci_bus], [device.pci_dev], PCI_REG_IRQ |
mov [device.irq_line], al |
} |
macro PCI_find_rev { |
stdcall PciRead8, [device.pci_bus], [device.pci_dev], PCI_REVISION_ID |
mov [device.revision], al |
} |
macro PCI_make_bus_master bus, dev { |
stdcall PciRead32, [device.pci_bus], [device.pci_dev], PCI_REG_COMMAND |
or al, PCI_BIT_MASTER |
stdcall PciWrite32, [device.pci_bus], [device.pci_dev], PCI_REG_COMMAND, eax |
} |
macro PCI_adjust_latency min { |
local .not |
stdcall PciRead8, [device.pci_bus], [device.pci_dev], PCI_REG_LATENCY |
cmp al, min |
ja .not |
mov al, min |
stdcall PciWrite8, [device.pci_bus], [device.pci_dev], PCI_REG_LATENCY, eax |
.not: |
} |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |
/drivers/proc32.inc |
---|
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |
/drivers/struct.inc |
---|
0,0 → 1,240 |
; Macroinstructions for defining data structures |
macro struct name |
{ virtual at 0 |
fields@struct equ name |
match child parent, name \{ fields@struct equ child,fields@\#parent \} |
sub@struct equ |
struc db [val] \{ \common define field@struct .,db,<val> |
fields@struct equ fields@struct,field@struct \} |
struc dw [val] \{ \common define field@struct .,dw,<val> |
fields@struct equ fields@struct,field@struct \} |
struc du [val] \{ \common define field@struct .,du,<val> |
fields@struct equ fields@struct,field@struct \} |
struc dd [val] \{ \common define field@struct .,dd,<val> |
fields@struct equ fields@struct,field@struct \} |
struc dp [val] \{ \common define field@struct .,dp,<val> |
fields@struct equ fields@struct,field@struct \} |
struc dq [val] \{ \common define field@struct .,dq,<val> |
fields@struct equ fields@struct,field@struct \} |
struc dt [val] \{ \common define field@struct .,dt,<val> |
fields@struct equ fields@struct,field@struct \} |
struc rb count \{ define field@struct .,db,count dup (?) |
fields@struct equ fields@struct,field@struct \} |
struc rw count \{ define field@struct .,dw,count dup (?) |
fields@struct equ fields@struct,field@struct \} |
struc rd count \{ define field@struct .,dd,count dup (?) |
fields@struct equ fields@struct,field@struct \} |
struc rp count \{ define field@struct .,dp,count dup (?) |
fields@struct equ fields@struct,field@struct \} |
struc rq count \{ define field@struct .,dq,count dup (?) |
fields@struct equ fields@struct,field@struct \} |
struc rt count \{ define field@struct .,dt,count dup (?) |
fields@struct equ fields@struct,field@struct \} |
macro db [val] \{ \common \local anonymous |
define field@struct anonymous,db,<val> |
fields@struct equ fields@struct,field@struct \} |
macro dw [val] \{ \common \local anonymous |
define field@struct anonymous,dw,<val> |
fields@struct equ fields@struct,field@struct \} |
macro du [val] \{ \common \local anonymous |
define field@struct anonymous,du,<val> |
fields@struct equ fields@struct,field@struct \} |
macro dd [val] \{ \common \local anonymous |
define field@struct anonymous,dd,<val> |
fields@struct equ fields@struct,field@struct \} |
macro dp [val] \{ \common \local anonymous |
define field@struct anonymous,dp,<val> |
fields@struct equ fields@struct,field@struct \} |
macro dq [val] \{ \common \local anonymous |
define field@struct anonymous,dq,<val> |
fields@struct equ fields@struct,field@struct \} |
macro dt [val] \{ \common \local anonymous |
define field@struct anonymous,dt,<val> |
fields@struct equ fields@struct,field@struct \} |
macro rb count \{ \local anonymous |
define field@struct anonymous,db,count dup (?) |
fields@struct equ fields@struct,field@struct \} |
macro rw count \{ \local anonymous |
define field@struct anonymous,dw,count dup (?) |
fields@struct equ fields@struct,field@struct \} |
macro rd count \{ \local anonymous |
define field@struct anonymous,dd,count dup (?) |
fields@struct equ fields@struct,field@struct \} |
macro rp count \{ \local anonymous |
define field@struct anonymous,dp,count dup (?) |
fields@struct equ fields@struct,field@struct \} |
macro rq count \{ \local anonymous |
define field@struct anonymous,dq,count dup (?) |
fields@struct equ fields@struct,field@struct \} |
macro rt count \{ \local anonymous |
define field@struct anonymous,dt,count dup (?) |
fields@struct equ fields@struct,field@struct \} |
macro union \{ fields@struct equ fields@struct,,union,< |
sub@struct equ union \} |
macro struct \{ fields@struct equ fields@struct,,substruct,< |
sub@struct equ substruct \} } |
macro ends |
{ match , sub@struct \{ restruc db,dw,du,dd,dp,dq,dt |
restruc rb,rw,rd,rp,rq,rt |
purge db,dw,du,dd,dp,dq,dt |
purge rb,rw,rd,rp,rq,rt |
purge union,struct |
match name tail,fields@struct, \\{ if $ |
display 'Error: definition of ',\\`name,' contains illegal instructions.',0Dh,0Ah |
err |
end if \\} |
match name=,fields,fields@struct \\{ fields@struct equ |
make@struct name,fields |
define fields@\\#name fields \\} |
end virtual \} |
match any, sub@struct \{ fields@struct equ fields@struct> \} |
restore sub@struct } |
macro make@struct name,[field,type,def] |
{ common |
local define |
define equ name |
forward |
local sub |
match , field \{ make@substruct type,name,sub def |
define equ define,.,sub, \} |
match any, field \{ define equ define,.#field,type,<def> \} |
common |
match fields, define \{ define@struct fields \} } |
macro define@struct name,[field,type,def] |
{ common |
virtual |
db `name |
load initial@struct byte from 0 |
if initial@struct = '.' |
display 'Error: name of structure should not begin with a dot.',0Dh,0Ah |
err |
end if |
end virtual |
local list |
list equ |
forward |
if ~ field eq . |
name#field type def |
sizeof.#name#field = $ - name#field |
else |
label name#.#type |
rb sizeof.#type |
end if |
local value |
match any, list \{ list equ list, \} |
list equ list <value> |
common |
sizeof.#name = $ |
restruc name |
match values, list \{ |
struc name value \\{ \\local \\..base |
match any, fields@struct \\\{ fields@struct equ fields@struct,.,name,<values> \\\} |
match , fields@struct \\\{ label \\..base |
forward |
match , value \\\\{ field type def \\\\} |
match any, value \\\\{ field type value |
if ~ field eq . |
rb sizeof.#name#field - ($-field) |
end if \\\\} |
common label . at \\..base \\\} |
\\} |
macro name value \\{ |
match any, fields@struct \\\{ \\\local anonymous |
fields@struct equ fields@struct,anonymous,name,<values> \\\} |
match , fields@struct \\\{ |
forward |
match , value \\\\{ type def \\\\} |
match any, value \\\\{ \\\\local ..field |
..field = $ |
type value |
if ~ field eq . |
rb sizeof.#name#field - ($-..field) |
end if \\\\} |
common \\\} \\} \} } |
macro enable@substruct |
{ macro make@substruct substruct,parent,name,[field,type,def] |
\{ \common |
\local define |
define equ parent,name |
\forward |
\local sub |
match , field \\{ match any, type \\\{ enable@substruct |
make@substruct type,parent,sub def |
purge make@substruct |
define equ define,.,sub, \\\} \\} |
match any, field \\{ define equ define,.\#field,type,<def> \\} |
\common |
match fields, define \\{ define@\#substruct fields \\} \} } |
enable@substruct |
macro define@union parent,name,[field,type,def] |
{ common |
virtual at parent#.#name |
forward |
if ~ field eq . |
virtual at parent#.#name |
parent#field type def |
sizeof.#parent#field = $ - parent#field |
end virtual |
if sizeof.#parent#field > $ - parent#.#name |
rb sizeof.#parent#field - ($ - parent#.#name) |
end if |
else |
virtual at parent#.#name |
label parent#.#type |
type def |
end virtual |
label name#.#type at parent#.#name |
if sizeof.#type > $ - parent#.#name |
rb sizeof.#type - ($ - parent#.#name) |
end if |
end if |
common |
sizeof.#name = $ - parent#.#name |
end virtual |
struc name [value] \{ \common |
label .\#name |
last@union equ |
forward |
match any, last@union \\{ virtual at .\#name |
field type def |
end virtual \\} |
match , last@union \\{ match , value \\\{ field type def \\\} |
match any, value \\\{ field type value \\\} \\} |
last@union equ field |
common rb sizeof.#name - ($ - .\#name) \} |
macro name [value] \{ \common \local ..anonymous |
..anonymous name value \} } |
macro define@substruct parent,name,[field,type,def] |
{ common |
virtual at parent#.#name |
forward |
if ~ field eq . |
parent#field type def |
sizeof.#parent#field = $ - parent#field |
else |
label parent#.#type |
rb sizeof.#type |
end if |
common |
sizeof.#name = $ - parent#.#name |
end virtual |
struc name value \{ |
label .\#name |
forward |
match , value \\{ field type def \\} |
match any, value \\{ field type value |
if ~ field eq . |
rb sizeof.#parent#field - ($-field) |
end if \\} |
common \} |
macro name value \{ \local ..anonymous |
..anonymous name \} } |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |