;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                                    ;;
;; Copyright (C) KolibriOS team 2004-2014. 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 '../struct.inc'
include '../macros.inc'
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, [edx + IOCTL.io_code]
        test    eax, eax        ; check for SRV_GETVERSION
        jnz     @f
; 3. This is SRV_GETVERSION request, no input, 4 bytes output, API_VERSION.
; 3a. Output size must be at least 4 bytes.
        cmp     [edx + IOCTL.out_size], 4
        jb      .fail
; 3b. Write result to the output buffer.
        mov     eax, [edx + IOCTL.output]
        mov     [eax], dword API_VERSION
; 3c. Return success.
        xor     eax, eax
        ret     4
  @@:
        dec     eax     ; check for SRV_HOOK
        jnz     .fail
; 4. This is SRV_HOOK request, input defines the device to hook, no output.
; 4a. The driver works only with PCI devices,
;       so input must be at least 3 bytes long.
        cmp     [edx + IOCTL.inp_size], 3
        jb      .fail
; 4b. First byte of input is bus type, 1 stands for PCI.
        mov     eax, [edx + IOCTL.input]
        cmp     byte [eax], 1
        jne     .fail
; 4c. Second and third bytes of the input define the device: bus and dev.
;       Word in bx holds both bytes.
        mov     bx, [eax+1]
; 4d. Check if the device was already hooked,
;       scan through the list of known devices.
; check if the device is already listed
        mov     esi, device_list
        mov     ecx, [devices]
        test    ecx, ecx
        jz      .firstdevice

;        mov     eax, [edx + IOCTL.input]               ; get the pci bus and device numbers
        mov     ax, [eax+1]                            ;
  .nextdevice:
        mov     ebx, [esi]
        cmp     al, byte[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, [edx + 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