;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                                 ;;
;; Copyright (C) KolibriOS team 2004-2014. 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 PE DLL native
entry START

        CURRENT_API             = 0x0200
        COMPATIBLE_API          = 0x0100
        API_VERSION             = (COMPATIBLE_API shl 16) + CURRENT_API

        MAX_DEVICES             = 16

        DEBUG                   = 1
        __DEBUG__               = 1
        __DEBUG_LEVEL__         = 2             ; 1 = verbose, 2 = errors only

section '.flat' readable writable executable

include '../proc32.inc'
include '../struct.inc'
include '../macros.inc'
include '../fdo.inc'
include '../netdrv.inc'

; 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

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

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

struct  rxfd

        status          dw ?
        command         dw ?
        link            dd ?
        rx_buf_addr     dd ?
        count           dw ?
        size            dw ?
        packet          rb 1500

ends

struc   txfd {

        .status         dw ?
        .command        dw ?
        .link           dd ?
        .desc_addr      dd ?
        .count          dd ?

        .buf_addr0      dd ?
        .buf_size0      dd ?

}

struc   confcmd {

        .status         dw ?
        .command        dw ?
        .link           dd ?
        .data           rb 64

}

struc   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 ?

}

struct  device          ETH_DEVICE

        io_addr         dd ?
        pci_bus         dd ?
        pci_dev         dd ?
        rx_desc         dd ?
        last_tx_buffer  dd ?
        ee_bus_width    db ?
        irq_line        db ?

        rb 0x100 - ($ and 0xff) ; align 256
        txfd            txfd

        rb 0x100 - ($ and 0xff) ; align 256
        confcmd         confcmd

        rb 0x100 - ($ and 0xff) ; align 256
        lstats          lstats

ends

;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                        ;;
;; proc START             ;;
;;                        ;;
;; (standard driver proc) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;

proc START c, state:dword

        cmp [state], 1
        jne .exit

  .entry:

        DEBUGF 1,"Loading driver\n"
        invoke  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, [edx + IOCTL.io_code]

;------------------------------------------------------

        cmp     eax, 0 ;SRV_GETVERSION
        jne     @F

        cmp     [edx + IOCTL.out_size], 4
        jb      .fail
        mov     eax, [edx + IOCTL.output]
        mov     [eax], dword API_VERSION

        xor     eax, eax
        ret

;------------------------------------------------------
  @@:
        cmp     eax, 1 ;SRV_HOOK
        jne     .fail

        cmp     [edx + IOCTL.inp_size], 3               ; Data input must be at least 3 bytes
        jb      .fail

        mov     eax, [edx + IOCTL.input]
        cmp     byte [eax], 1                           ; 1 means device number and bus number (pci) are given
        jne     .fail                                   ; other types arent supported for this card yet

; check if the device is already listed

        mov     esi, device_list
        mov     ecx, [devices]
        test    ecx, ecx
        jz      .firstdevice

;        mov     eax, [edx + IOCTL.input]                ; get the pci bus and device numbers
        mov     ax , [eax+1]                            ;
  .nextdevice:
        mov     ebx, [esi]
        cmp     al, byte[ebx + device.pci_bus]
        jne     @f
        cmp     ah, byte[ebx + device.pci_dev]
        je      .find_devicenum                         ; Device is already loaded, let's find it's device number
       @@:
        add     esi, 4
        loop    .nextdevice


; This device doesnt have its own eth_device structure yet, lets create one
  .firstdevice:
        cmp     [devices], MAX_DEVICES                  ; First check if the driver can handle one more card
        jae     .fail

        allocate_and_clear ebx, sizeof.device, .fail      ; Allocate the buffer for device structure

; Fill in the direct call addresses into the struct

        mov     [ebx + device.reset], reset
        mov     [ebx + device.transmit], transmit
        mov     [ebx + device.unload], unload
        mov     [ebx + device.name], my_service

; save the pci bus and device numbers

        mov     eax, [edx + IOCTL.input]
        movzx   ecx, byte[eax+1]
        mov     [ebx + device.pci_bus], ecx
        movzx   ecx, byte[eax+2]
        mov     [ebx + device.pci_dev], ecx

; Now, it's time to find the base io addres of the PCI device

        stdcall PCI_find_io, [ebx + device.pci_bus], [ebx + device.pci_dev]
        mov     [ebx + device.io_addr], eax

; We've found the io address, find IRQ now

        invoke  PciRead8, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.interrupt_line
        mov     [ebx + device.irq_line], al

        DEBUGF  1,"Hooking into device, dev:%x, bus:%x, irq:%x, addr:%x\n",\
        [ebx + device.pci_dev]:1,[ebx + device.pci_bus]:1,[ebx + device.irq_line]:1,[ebx + device.io_addr]:4

; Ok, the eth_device structure is ready, let's probe the device

        pushf
        cli                     ; disable ints untilm 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     [ebx + device.type], NET_TYPE_ETH
        invoke  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"
        invoke  NetPtrToNum                                             ; This kernel procedure converts a pointer to device struct in ebx
                                                                        ; into a device number in edi
        mov     eax, edi                                                ; Application wants it in eax instead
        DEBUGF  2,"Kernel says: %u\n", eax
        ret

; If an error occured, remove all allocated data and exit (returning -1 in eax)

  .err:
        invoke  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\n"

; Make the device a bus master
        invoke  PciRead32, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.command
        or      al, PCI_CMD_MASTER
        invoke  PciWrite32, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.command, eax

;---------------------------
; First, identify the device

        invoke  PciRead32, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header.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  2,"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, [ebx + device.irq_line]
        DEBUGF  1,"Attaching int handler to irq %x\n", eax:1
        invoke  AttachIntHandler, eax, int_handler, ebx
        test    eax, eax
        jnz     @f
        DEBUGF  2,"Could not attach int handler!\n"
        or      eax, -1
        ret
  @@:

        DEBUGF  1,"Resetting\n"

;---------------
; reset the card

        set_io  [ebx + device.io_addr], 0
        set_io  [ebx + device.io_addr], reg_port
        xor     eax, eax        ; Software Reset
        out     dx, eax

        mov     esi, 10
        invoke  Sleep           ; Give the card time to warm up.

;---------------------------------
; Tell device where to store stats

        lea     eax, [ebx + device.lstats.tx_good_frames]      ; lstats
        invoke  GetPhysAddr
        set_io  [ebx + device.io_addr], 0
        set_io  [ebx + device.io_addr], reg_scb_ptr
        out     dx, eax

        mov     ax, INT_MASK + CU_STATSADDR
        set_io  [ebx + device.io_addr], reg_scb_cmd
        out     dx, ax
        call    cmd_wait

;-----------------
; setup RX

        set_io  [ebx + device.io_addr], reg_scb_ptr
        xor     eax, eax
        out     dx, eax

        set_io  [ebx + device.io_addr], 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  [ebx + device.io_addr], 0
        set_io  [ebx + device.io_addr], reg_scb_ptr
        mov     eax, [ebx + device.rx_desc]
        invoke  GetPhysAddr
        out     dx, eax

        mov     ax, INT_MASK + RX_START
        set_io  [ebx + device.io_addr], reg_scb_cmd
        out     dx, ax
        call    cmd_wait

; Set-up TX

        set_io  [ebx + device.io_addr], reg_scb_ptr
        xor     eax, eax
        out     dx, eax

        set_io  [ebx + device.io_addr], reg_scb_cmd
        mov     ax, INT_MASK + CU_CMD_BASE
        out     dx, ax
        call    cmd_wait

;  --------------------

        mov     [ebx + device.confcmd.command], CmdConfigure + Cmdsuspend
        mov     [ebx + device.confcmd.status], 0
        lea     eax, [ebx + device.txfd.status]
        invoke  GetPhysAddr
        mov     [ebx + device.confcmd.link], eax

        mov     esi, confcmd_data
        lea     edi, [ebx + device.confcmd.data]
        mov     ecx, 22
        rep     movsb

        mov     byte[ebx + device.confcmd.data + 1], 0x88  ; fifo of 8 each
        mov     byte[ebx + device.confcmd.data + 4], 0
        mov     byte[ebx + device.confcmd.data + 5], 0x80
        mov     byte[ebx + device.confcmd.data + 15], 0x48
        mov     byte[ebx + device.confcmd.data + 19], 0x80
        mov     byte[ebx + device.confcmd.data + 21], 0x05

        mov     [ebx + device.txfd.command], CmdIASetup
        mov     [ebx + device.txfd.status], 0
        lea     eax, [ebx + device.confcmd.status]
        invoke  GetPhysAddr
        mov     [ebx + device.txfd.link], eax

;;; copy in our MAC

        lea     edi, [ebx + device.txfd.desc_addr]
        lea     esi, [ebx + device.mac]
        movsd
        movsw

        set_io  [ebx + device.io_addr], reg_scb_ptr
        lea     eax, [ebx + device.txfd.status]
        invoke  GetPhysAddr
        out     dx, eax

; Start CU & enable ints

        set_io  [ebx + device.io_addr], reg_scb_cmd
        mov     ax, CU_START
        out     dx, ax
        call    cmd_wait

;-----------------------
; build txfd structure (again!)

        lea     eax, [ebx + device.txfd.status]
        invoke  GetPhysAddr
        mov     [ebx + device.txfd.link], eax
        mov     [ebx + device.txfd.count], 0x02208000
        lea     eax, [ebx + device.txfd.buf_addr0]
        invoke  GetPhysAddr
        mov     [ebx + device.txfd.desc_addr], eax

; Indicate that we have successfully reset the card

        DEBUGF  1,"Reset complete\n"

        mov     [ebx + device.mtu], 1514

; Set link state to unknown
        mov     [ebx + device.state], ETH_LINK_UNKNOWN

        xor     eax, eax        ; indicate that we have successfully reset the card
        ret


align 4
create_ring:

        DEBUGF  1,"Creating ring\n"

;---------------------
; build rxfd structure

        invoke  KernelAlloc, 2000
        mov     [ebx + device.rx_desc], eax
        mov     esi, eax
        invoke  GetPhysAddr
        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, [ebx + device.txfd.status]
        invoke  GetPhysAddr
        mov     [ebx + device.txfd.link], eax
        mov     [ebx + device.txfd.count], 0x01208000
        lea     eax, [ebx + device.txfd.buf_addr0]
        invoke  GetPhysAddr
        mov     [ebx + device.txfd.desc_addr], eax

        ret



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                         ;;
;; Transmit                                ;;
;;                                         ;;
;; In: pointer to device structure in ebx  ;;
;;                                         ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

proc transmit stdcall bufferptr, buffersize

        pushf
        cli

        DEBUGF  1,"Transmitting packet, buffer:%x, size:%u\n", [bufferptr], [buffersize]
        mov     eax, [bufferptr]
        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     [buffersize], 1514
        ja      .fail
        cmp     [buffersize], 60
        jb      .fail

        ;;; TODO: check if current descriptor is in use
        ; fill in buffer address and size
        mov     [ebx + device.last_tx_buffer], eax
        invoke  GetPhysAddr
        mov     [ebx + device.txfd.buf_addr0], eax
        mov     ecx, [buffersize]
        mov     [ebx + device.txfd.buf_size0], ecx

        mov     [ebx + device.txfd.status], 0
        mov     [ebx + device.txfd.command], Cmdsuspend + CmdTx + CmdTxFlex + 1 shl 15 ;;; EL bit
 ;       mov     [txfd.count], 0x02208000   ;;;;;;;;;;;

        ; Inform device of the new/updated transmit descriptor
        lea     eax, [ebx + device.txfd.status]
        invoke  GetPhysAddr
        set_io  [ebx + device.io_addr], 0
        set_io  [ebx + device.io_addr], reg_scb_ptr
        out     dx, eax

        ; Start the transmit
        mov     ax, CU_START
        set_io  [ebx + device.io_addr], reg_scb_cmd
        out     dx, ax
        call    cmd_wait

; Update stats
        inc     [ebx + device.packets_tx]
        mov     ecx, [buffersize]
        add     dword[ebx + device.bytes_tx], ecx
        adc     dword[ebx + device.bytes_tx + 4], 0

        DEBUGF  1,"Transmit OK\n"
        popf
        xor     eax, eax
        ret

  .fail:
        invoke  KernelFree, [bufferptr]
        popf
        or      eax, -1
        ret

endp


;;;;;;;;;;;;;;;;;;;;;;;
;;                   ;;
;; Interrupt handler ;;
;;                   ;;
;;;;;;;;;;;;;;;;;;;;;;;

align 4
int_handler:

        push    ebx esi edi

        DEBUGF  1,"INT\n"

; find pointer of device wich made IRQ occur

        mov     ecx, [devices]
        test    ecx, ecx
        jz      .nothing
        mov     esi, device_list
  .nextdevice:
        mov     ebx, [esi]

;        set_io  [ebx + device.io_addr], 0              ; reg_scb_status = 0
        set_io  [ebx + device.io_addr], 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, [ebx + 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 [ebx + device.bytes_rx], ecx
        adc     dword [ebx + device.bytes_rx + 4], 0
        inc     dword [ebx + device.packets_rx]

; allocate new descriptor

        invoke  KernelAlloc, 2000
        mov     [ebx + device.rx_desc], eax
        mov     esi, eax
        invoke  GetPhysAddr
        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  [ebx + device.io_addr], 0
        set_io  [ebx + device.io_addr], reg_scb_ptr
;        lea     eax, [ebx + device.rx_desc]
;        invoke  GetPhysAddr
        out     dx, eax

        set_io  [ebx + device.io_addr], 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     [ebx + device.txfd.status], 0
        je      .done
        cmp     [ebx + device.last_tx_buffer], 0
        je      .done
        push    ax
        DEBUGF  1, "Removing packet 0x%x from RAM!\n", [ebx + device.last_tx_buffer]
        invoke  KernelFree, [ebx + device.last_tx_buffer]
        mov     [ebx + device.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

        invoke  KernelAlloc, 2000
        mov     [ebx + device.rx_desc], eax
        mov     esi, eax
        invoke  GetPhysAddr
        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  [ebx + device.io_addr], 0
        set_io  [ebx + device.io_addr], reg_scb_ptr
;        lea     eax, [ebx + device.rx_desc]
;        invoke  GetPhysAddr
        out     dx, eax

        set_io  [ebx + device.io_addr], 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\n", esi

        set_io  [ebx + device.io_addr], 0
        set_io  [ebx + device.io_addr], 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, [ebx + device.ee_bus_width]
        shl     esi, cl
        or      esi, EE_READ shl 29

        movzx   ecx, [ebx + device.ee_bus_width]
        add     ecx, 3

        mov     al, EE_CS
        out     dx, al
        call    udelay

;-----------------------
; Write this to the chip

  .loop:
        mov     al, EE_CS + EE_SK
        shl     esi, 1
        jnc     @f
        or      al, EE_DI
       @@:
        out     dx, al
        call    udelay

        and     al, not EE_SK
        out     dx, al
        call    udelay

        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
        call    udelay

        in      al, dx
        test    al, EE_DO
        jz      @f
        inc     esi
       @@:

        mov     al, EE_CS
        out     dx, al
        call    udelay

        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  [ebx + device.io_addr], 0
        set_io  [ebx + device.io_addr], 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, [ebx + device.ee_bus_width]
        shl     esi, cl
        or      esi, EE_WRITE shl 29

        movzx   ecx, [ebx + 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
        call    udelay

        and     al, not EE_SK
        out     dx, al
        call    udelay

        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
        call    udelay

        and     al, not EE_SK
        out     dx, al
        call    udelay

        loop    .loop2

;-----------------------
; de-activate the eeprom

        xor     al, al
        out     dx, al


        ret



align 4
ee_get_width:

        set_io  [ebx + device.io_addr], 0
        set_io  [ebx + device.io_addr], reg_eeprom

        mov     al, EE_CS      ; activate eeprom
        out     dx, al
        call    udelay

        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
        call    udelay

        and     al, not EE_SK
        out     dx, al
        call    udelay

        inc     ecx

        cmp     ecx, 15
        jae     .give_up

        in      al, dx
        test    al, EE_DO
        jnz     .loop

        xor     al, al
        out     dx, al          ; de-activate eeprom

        sub     cl, 3           ; dont count the opcode bits
        mov     [ebx + device.ee_bus_width], cl
        DEBUGF  1, "Eeprom width=%u bit\n", ecx

        ret

  .give_up:
        DEBUGF  2, "Eeprom not found!\n"

        xor     al, al
        out     dx, al          ; de-activate eeprom

        ret


; Wait a minimum of 2�s
udelay:
        pusha
        mov     esi, 1
        invoke  Sleep
        popa

        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  [ebx + device.io_addr], 0
        set_io  [ebx + device.io_addr], reg_mdi_ctrl
        out     dx, eax

  .wait:
        call    udelay
        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  [ebx + device.io_addr], 0
        set_io  [ebx + device.io_addr], reg_mdi_ctrl
        out     dx, eax

  .wait:
        call    udelay
        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[ebx + device.mac], si

        mov     esi, 1
        call    ee_read
        mov     word[ebx + device.mac+2], si

        mov     esi, 2
        call    ee_read
        mov     word[ebx + device.mac+4], si


        ret


align 4
MAC_write:

;;;;

        ret




; End of code


data fixups
end data

include '../peimport.inc'

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

include_debug_strings                           ; All data wich FDO uses will be included here

align 4
devices         dd 0                              ; number of currently running devices
device_list     rd MAX_DEVICES                    ; This list contains all pointers to device structures the driver is handling