$Revision$
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                                 ;;
;; Copyright (C) KolibriOS team 2004-2007. All rights reserved.    ;;
;; Distributed under terms of the GNU General Public License       ;;
;;                                                                 ;;
;;  ETHERNET.INC                                                   ;;
;;                                                                 ;;
;;  Ethernet network layer for Menuet OS                           ;;
;;                                                                 ;;
;;  Version 0.4  22 September 2003                                 ;;
;;                                                                 ;;
;;  This file contains the following:                              ;;
;;      PCI bus scanning for valid devices                         ;;
;;      Table of supported ethernet drivers                        ;;
;;      Code to identify and activate a supported driver           ;;
;;      ARP handler                                                ;;
;;      Driver interface to the IP layer                           ;;
;;      Gateway support                                            ;;
;;                                                                 ;;
;;  Individual driver files are included here                      ;;
;;                                                                 ;;
;;  The PCI bus scanning code was ported from the etherboot        ;;
;;  5.0.6 project. The copyright statement for that code is        ;;
;;                                                                 ;;
;;          GNU GENERAL PUBLIC LICENSE                             ;;
;;             Version 2, June 1991                                ;;
;;                                                                 ;;
;;  remaining parts Copyright 2002 Mike Hibbett                    ;;
;;   mikeh@oceanfree.net                                           ;;
;;                                                                 ;;
;;  See file COPYING for details                                   ;;
;;                                                                 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;********************************************************************
;   Interface
;      ethernet_driver   called by stack_handler in stack.inc
;      eth_probe         called by app_stack_handler in stack.inc
;
;********************************************************************

ETHER_IP		    equ     0x0008	; Reversed from 0800 for intel
ETHER_ARP		    equ     0x0608	; Reversed from 0806 for intel
ETHER_RARP		    equ     0x3580

struc ETH_FRAME
{  .DstMAC	 dp   ?  ;destination MAC-address [6 bytes]
   .SrcMAC	 dp   ?  ;source MAC-address [6 bytes]
   .Type	 dw   ?  ;type of the upper-layer protocol [2 bytes]
   .Data	 db   ?  ;data [46-1500 bytes]
}

virtual at Ether_buffer
  ETH_FRAME ETH_FRAME
end virtual


; Some useful information on data structures

;     Ethernet Packet - ARP Request example
;
;   0                   1                   2                   3
;   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
;
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   |       Dest   H/W Address                                      |
;   |                    ( 14 byte header )                         |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   |                               |     Source     H/W Address    |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   |                                                               |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   |    Protocol - ARP 08  06      |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   |  H/W Type  00           01    |  Protocol Type   08 00        |
;   |                   ( ARP Request packet )                      |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   | HLen    0x06  | PLen    0x04  |    OpCode        00   01      |
;   |               ( 0001 for request, 0002 for reply )            |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   | Source Hardware Address ( MAC Address )                       |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   |                               |  Source IP Address            |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   |                               | Destination Hardware Address  |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   |                                                               |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   | Destination IP Address                                        |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

; Include individual drivers source files at this point.
; If you create a new driver, include it below.

include "drivers/rtl8029.inc"
include "drivers/i8255x.inc"
include "drivers/rtl8139.inc"
include "drivers/3c59x.inc"
include "drivers/sis900.inc"
include "drivers/pcnet32.inc"
include "drivers/rtl8169.inc"

; PCICards
; ========
; PCI vendor and hardware types for hardware supported by the above drivers
; If you add a driver, ensure you update this datastructure, otherwise the
; card will not be probed.
; Each driver is defined by 4 double words. These are
;   PCIVendorDevice  probeFunction ResetFunction PollFunction transmitFunction
; The last entry must be kept at all zeros, to indicate the end of the list
; As a PCI driver may support more than one hardware implementation, there may
; be several lines which refer to the same functions.
; The first driver found on the PCI bus will be the one used.

PCICARDS_ENTRY_SIZE	    equ     24	  ; Size of each PCICARDS entry

iglobal
PCICards:
dd  0x12098086, I8255x_probe, I8255x_reset, I8255x_poll, I8255x_transmit, 0
dd  0x10298086, I8255x_probe, I8255x_reset, I8255x_poll, I8255x_transmit, 0
dd  0x12298086, I8255x_probe, I8255x_reset, I8255x_poll, I8255x_transmit, 0
dd  0x10308086, I8255x_probe, I8255x_reset, I8255x_poll, I8255x_transmit, 0
dd  0x24498086, I8255x_probe, I8255x_reset, I8255x_poll, I8255x_transmit, 0

dd  0x802910ec, rtl8029_probe, rtl8029_reset, rtl8029_poll, rtl8029_transmit, 0

dd  0x813910ec, rtl8139_probe, rtl8139_reset, rtl8139_poll, rtl8139_transmit, rtl8139_cable  ; tested by hidnplayr: works ok
dd  0x813810ec, rtl8139_probe, rtl8139_reset, rtl8139_poll, rtl8139_transmit, rtl8139_cable
dd  0x12111113, rtl8139_probe, rtl8139_reset, rtl8139_poll, rtl8139_transmit, rtl8139_cable  ; tested by hidnplayr: works ok
dd  0x13601500, rtl8139_probe, rtl8139_reset, rtl8139_poll, rtl8139_transmit, rtl8139_cable
dd  0x13604033, rtl8139_probe, rtl8139_reset, rtl8139_poll, rtl8139_transmit, rtl8139_cable
dd  0x13001186, rtl8139_probe, rtl8139_reset, rtl8139_poll, rtl8139_transmit, rtl8139_cable
dd  0x13401186, rtl8139_probe, rtl8139_reset, rtl8139_poll, rtl8139_transmit, rtl8139_cable
dd  0xab0613d1, rtl8139_probe, rtl8139_reset, rtl8139_poll, rtl8139_transmit, rtl8139_cable
dd  0xa1171259, rtl8139_probe, rtl8139_reset, rtl8139_poll, rtl8139_transmit, rtl8139_cable
dd  0xa11e1259, rtl8139_probe, rtl8139_reset, rtl8139_poll, rtl8139_transmit, rtl8139_cable
dd  0xab0614ea, rtl8139_probe, rtl8139_reset, rtl8139_poll, rtl8139_transmit, rtl8139_cable
dd  0xab0714ea, rtl8139_probe, rtl8139_reset, rtl8139_poll, rtl8139_transmit, rtl8139_cable
dd  0x123411db, rtl8139_probe, rtl8139_reset, rtl8139_poll, rtl8139_transmit, rtl8139_cable
dd  0x91301432, rtl8139_probe, rtl8139_reset, rtl8139_poll, rtl8139_transmit, rtl8139_cable
dd  0x101202ac, rtl8139_probe, rtl8139_reset, rtl8139_poll, rtl8139_transmit, rtl8139_cable
dd  0x0106018a, rtl8139_probe, rtl8139_reset, rtl8139_poll, rtl8139_transmit, rtl8139_cable
dd  0x1211126c, rtl8139_probe, rtl8139_reset, rtl8139_poll, rtl8139_transmit, rtl8139_cable
dd  0x81391743, rtl8139_probe, rtl8139_reset, rtl8139_poll, rtl8139_transmit, rtl8139_cable
dd  0x8139021b, rtl8139_probe, rtl8139_reset, rtl8139_poll, rtl8139_transmit, rtl8139_cable

dd  0x816810ec, rtl8169_probe, rtl8169_reset, rtl8169_poll, rtl8169_transmit, 0
dd  0x816910ec, rtl8169_probe, rtl8169_reset, rtl8169_poll, rtl8169_transmit, 0
dd  0x011616ec, rtl8169_probe, rtl8169_reset, rtl8169_poll, rtl8169_transmit, 0
dd  0x43001186, rtl8169_probe, rtl8169_reset, rtl8169_poll, rtl8169_transmit, 0

dd  0x590010b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x592010b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x597010b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x595010b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x595110b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x595210b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x900010b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x900110b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x900410b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x900510b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x900610b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x900A10b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x905010b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x905110b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x905510b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x905810b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x905A10b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x920010b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x980010b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x980510b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x764610b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x505510b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x605510b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x605610b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x5b5710b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x505710b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x515710b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x525710b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x656010b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x656210b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x656410b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0
dd  0x450010b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit, 0

dd  0x09001039, SIS900_probe, SIS900_reset, SIS900_poll, SIS900_transmit, 0

dd  0x20001022, pcnet32_probe, pcnet32_reset, pcnet32_poll, pcnet32_xmit, 0
dd  0x26251022, pcnet32_probe, pcnet32_reset, pcnet32_poll, pcnet32_xmit, 0
dd  0x20011022, pcnet32_probe, pcnet32_reset, pcnet32_poll, pcnet32_xmit, 0

;dd  0x08031516, mtd80x_probe, mtd80x_reset, mtd80x_poll, mtd80x_transmit, mtd80x_cable

; following cards are untested
dd  0x70161039, SIS900_probe, SIS900_reset, SIS900_poll, SIS900_transmit, 0
;dd  0x08001516, mtd80x_probe, mtd80x_reset, mtd80x_poll, mtd80x_transmit, mtd80x_cable
;dd  0x08911516, mtd80x_probe, mtd80x_reset, mtd80x_poll, mtd80x_transmit, mtd80x_cable

rb PCICARDS_ENTRY_SIZE	; end of list marker, do not remove
endg

uglobal
;Net-stack's interface's settings
  node_addr:	      db  0,0,0,0,0,0
  gateway_ip:	      dd  0
  dns_ip:	      dd  0

  eth_rx_data_len:    dw  0
  eth_status:	      dd  0
  io_addr:	      dd  0
  hdrtype:	      db  0
  vendor_device:      dd  0
  pci_data:	      dd  0
  pci_dev:	      dd  0
  pci_bus:	      dd  0

  ; These will hold pointers to the selected driver functions
  drvr_probe:	      dd  0
  drvr_reset:	      dd  0
  drvr_poll:	      dd  0
  drvr_transmit:      dd  0
  drvr_cable:	      dd  0

endg

iglobal
  broadcast_add:      db  0xff,0xff,0xff,0xff,0xff,0xff
  subnet_mask:	      dd  0x00ffffff   ; 255.255.255.0
endg

include "arp.inc"    ;arp-protocol functions
include "pci.inc"    ;PCI bus access functions


;***************************************************************************
;   Function
;      eth_tx
;
;   Description
;      Looks at the NET1OUT_QUEUE for data to send.
;      Stores that destination IP in a location used by the tx routine
;      Looks up the MAC address in the ARP table; stores that where
;      the tx routine can get it
;      Get the length of the data. Store that where the tx routine wants it
;      Call tx
;      Places buffer on empty queue when the tx routine finished
;
;***************************************************************************
proc eth_tx stdcall uses ebx esi edi
local MACAddress  dp  ?  ;allocate 6 bytes in the stack

    ; Look for a buffer to tx
    mov     eax, NET1OUT_QUEUE
    call    dequeue
    cmp     ax, NO_BUFFER
    je	    .exit	     ; Exit if no buffer available

    push    eax  ;save buffer number

    ; convert buffer pointer eax to the absolute address
    imul    eax, IPBUFFSIZE
    add     eax, IPbuffs

    ; Extract the destination IP
    ; find the destination IP in the ARP table, get MAC
    ; store this MAC in 'MACAddress'
    mov     ebx, eax		   ; Save buffer address
    mov     edx, [ebx + 16]	   ; get destination address

    ; If the destination address is 255.255.255.255,
    ; set the MACAddress to all ones ( broadcast )
    cld
    mov     esi, broadcast_add
    lea     edi, [MACAddress]
    movsd
    movsw
    cmp     edx, 0xffffffff
    je	    .send		 ; If it is broadcast, just send

    ; first, check destination IP to see if it is on 'this' network.
    ; The test is:
    ; if ( destIP & subnet_mask == stack_ip & subnet_mask )
    ;   destination is local
    ; else
    ;  destination is remote, so pass to gateway

    mov     eax, edx
    and     eax, [subnet_mask]
    mov     ecx, [stack_ip]
    and     ecx, [subnet_mask]
    cmp     eax, ecx
    je      .local

    mov     edx, [gateway_ip]
  .local:

    lea     eax, [MACAddress]	 ;cause this is local variable
    stdcall arp_table_manager, ARP_TABLE_IP_TO_MAC, edx, eax ;opcode,IP,MAC_ptr - Get the MAC address.

    cmp     eax, ARP_VALID_MAPPING
    je	    .send

    ; No valid entry. Has the request been sent, but timed out?
    cmp     eax, ARP_RESPONSE_TIMEOUT
    je	    .freebuf

  .wait_response:		    ;we wait arp-response
				    ; Re-queue the packet, and exit
    pop     ebx
    mov     eax, NET1OUT_QUEUE
    call    queue			 ; Get the buffer back
    jmp     .exit

  .send:    ;if ARP_VALID_MAPPING then send the packet
    lea     edi, [MACAddress]	  ; Pointer to 48 bit destination address
    movzx   ecx, word[ebx+2]	  ; Size of IP packet to send
    xchg    ch, cl		  ; because mirror byte-order
    mov     esi, ebx		  ; Pointer to packet data
    mov     bx, ETHER_IP	  ; Type of packet
    push    ebp
    call    dword [drvr_transmit] ; Call the drivers transmit function
    pop     ebp

    ; OK, we have sent a packet, so increment the count
    inc     dword [ip_tx_count]

    ; And finally, return the buffer to the free queue
  .freebuf:
    pop     eax
    call    freeBuff

  .exit:
    ret
endp

;***************************************************************************
;   Function
;      ether_IP_handler
;
;   Description
;      Called when an IP ethernet packet is received on the ethernet
;      Header + Data is in Ether_buffer[]
;      We just need to get a buffer from the 'free' queue, and
;      store the packet in it, then insert the packet number into the
;      IPRX queue.
;      If no queue entry is available, the packet is silently discarded
;      All registers may be destroyed
;
;***************************************************************************
ether_IP_handler:
    mov     eax, EMPTY_QUEUE
    call    dequeue
    cmp     ax, NO_BUFFER
    je	    eiph00x

    ; convert buffer pointer eax to the absolute address
    push    eax
    mov     ecx, IPBUFFSIZE
    mul     ecx
    add     eax, IPbuffs

    mov     edi, eax

    ; get a pointer to the start of the DATA
    mov     esi, ETH_FRAME.Data

    ; Now store it all away
    mov     ecx, IPBUFFSIZE / 4     ; Copy all of the available
			      ; data across - worse case
    cld
    rep     movsd

    ; And finally, place the buffer in the IPRX queue
    pop     ebx
    mov     eax, IPIN_QUEUE
    call    queue

eiph00x:
    ret

;***************************************************************************
;   Function
;      eth_probe
;   Description
;      Searches for an ethernet card. If found, the card is enabled and
;      the ethernet -> IP link established
;
;      This function scans the PCI bus looking for a supported device.
;      ISA bus is currently not supported.
;
;        eax is 0 if no hardware found
;***************************************************************************
eth_probe:
    ; Find a card on the PCI bus, and get it's address
    call    scan_bus			; Find the ethernet cards PIC address
    xor     eax, eax
    cmp     [io_addr], eax
    je	    ep_00x			; Return 0 in eax if no cards found

    call    dword [drvr_probe]		; Call the drivers probe function

    mov     eax, [io_addr]		; return a non zero value

ep_00x:
    ret

;***************************************************************************
;   Function
;      ethernet_driver
;
;   Description
;       The ethernet RX and TX handler
;       This is a kernel function, called by stack_handler
;
;***************************************************************************
ethernet_driver:
    ; Do nothing if the driver is inactive
    cmp     [ethernet_active], byte 0
    je	    eth_exit

    call    eth_rx
    call    eth_tx

eth_exit:
    ret

;***************************************************************************
;   Function
;      eth_rx
;
;   Description
;      Polls the ethernet card for received data. Extracts if present
;       Depending on the Protocol within the packet:
;         ARP : Pass to ARP_handler. This may result in an ARP reply
;               being tx'ed
;         IP  : Store in an IP buffer
;
;***************************************************************************
eth_rx:
    xor     ax, ax
    mov     [eth_rx_data_len], ax
    call    dword [drvr_poll]	    ; Call the drivers poll function

    mov     ax, [eth_rx_data_len]
    cmp     ax, 0
    je	    .exit


    ; Check the protocol. Call appropriate handler

    mov     ax, [ETH_FRAME.Type]    ; The address of the protocol word

    cmp     ax, ETHER_IP
    je	    .is_ip		     ; It's IP

    cmp     ax, ETHER_ARP
    je	    .is_arp		     ; It is ARP

    jmp     .exit		; If not IP or ARP, ignore

  .is_ip:
    DEBUGF  1,"K : eth_rx - IP packet\n"
    inc     dword [ip_rx_count]
    call    ether_IP_handler
    jmp     .exit

  .is_arp:
    DEBUGF  1,"K : eth_rx - ARP packet\n"
    ; At this point, the packet is still in the Ether_buffer
    call    arp_handler

  .exit:
    ret
