/*
 * dev_nic.h - NIC (Network Controler) generic device API definition.
 * 
 * Author  Alain Greiner    (2016,2017,2018,2019,2020)
 *
 * Copyright (c) UPMC Sorbonne Universites
 *
 * This file is part of ALMOS-MKH
 *
 * ALMOS-MKH is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2.0 of the License.
 *
 * ALMOS-MKH is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ALMOS-kernel; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef _DEV_NIC_H
#define _DEV_NIC_H

#include <kernel_config.h>
#include <hal_kernel_types.h>
#include <remote_busylock.h>
#include <remote_buf.h>
#include <xlist.h>

/****  Forward declarations  ****/

struct chdev_s;

/*****************************************************************************************
 *     Generic Network Interface Controler definition
 *
 * This device provides access to a generic Gigabit Ethernet network controler.
 * It assumes that the NIC hardware peripheral handles two packets queues for sent (TX)
 * and received (RX) packets.
 *
 * The supported protocols stack is : Ethernet / IPV4 / TCP or UDP
 * 
 * 1) hardware assumptions
 *
 * The NIC device is handling two (infinite) streams of packets to or from the network. 
 * It is the driver responsibility to move the RX packets from the NIC to the RX queue,
 * and the TX packets from the TX queue to the NIC.
 *
 * AS the RX and TX queues are independant, there is one NIC_RX device descriptor 
 * to handle RX packets, and another NIC_TX device descriptor to handle TX packets.
 *
 * In order to improve throughput, the NIC controller can implement multiple (N) channels.
 * In this case, the channel index is defined by an hash function computed from the remote
 * IP address and port. This index is computed by the hardware for an RX packet, and is 
 * computed by the kernel for a TX packet, using a specific driver function. 
 * The 2*N chdevs, and the associated server threads implementing the protocols stack,
 * are distributed in 2*N different clusters.
 *
 * 2) User API
 * 
 * On the user side, ALMOS-MKH implements the POSIX socket API. 
 * The following kernel functions implement the socket related syscalls :
 * - socket_build()     : create a local socket registered in process fd_array[].
 * - socket_bind()      : attach a local IP address and port to a local socket.
 * - socket_listen()    : local server makes a passive open.
 * - socket_connect()   : local client makes an active open to a remote server.
 * - socket_accept()    : local server accept a new remote client.
 * - socket_send()      : send data on a connected socket.
 * - socket_recv()      : receive data on a connected socket.
 * - socket_sendto()    : send a packet to a remote (IP address/port). 
 * - socket_recvfrom()  : receive a paket from a remote (IP address/port).
 *
 * 3) NIC TX and NIC_RX server threads
 *
 * The dev_nic_tx_server() & dev_nic_rx_server() functions defined below execute 
 * the user commands stored in the sockets to implement the [ETH / IP / TCP or UDP] 
 * protocols stack, as defined in the <ksocket.c> and <ksocket.h> files.
 *
 * 4) NIC driver API
 *
 * The generic NIC device "driver" API defines the following commands, used by the
 * NIC_TX and NIC_RX server threads, running in the cluster containing the relevant chdev,
 * to access the NIC_TX and NIC_RX packets queues:
 *
 * - READ     : consume one packet from the NIC_RX queue.
 * - WRITE    : produce one packet to the NIC_TX queue.
 *
 * All RX or TX paquets are sent or received in standard 2 Kbytes kernel buffers,
 * that are dynamically allocated by the protocols stack. 
 * The actual TX an RX queues structures depends on the hardware NIC implementation,
 * and are defined in the HAL specific driver code. 
 *
 * Moreover, the generic NIC device "driver" API defines the following commands,
 * used directly by a client thread running in any cluster, to access the NIC device
 * configuration or status registers:
 *
 * - GET_KEY      : get channel index from remote IP address and port 
 * - SET_RUN      : activate/desactivate one channel
 * - GET_INSTRU   : get one instrumentation counter value
 * - CLEAR_INSTRU : reset all instrumentation counters
 *
 * WARNING: the WTI mailboxes used by the driver ro receive events from the hardware
 * (available RX packet, or available free TX slot, for a given channel), must be 
 * statically allocated during the kernel initialisation phase, and must be 
 * routed to the cluster containing the associated TX/RX chdev and server thread.
 *
 *****************************************************************************************/

/****  Forward declarations  ****/

struct chdev_s;

/*****************************************************************************************
 *   Various constants used by the protocols stack
 ****************************************************************************************/

#define SRC_MAC_5              0x66         // This is a temporary short-cut for debug
#define SRC_MAC_4              0x55
#define SRC_MAC_3              0x44
#define SRC_MAC_2              0x33
#define SRC_MAC_1              0x22
#define SRC_MAC_0              0x11

#define DST_MAC_5              0x66         // This is a temporary short-cut for debug
#define DST_MAC_4              0x55
#define DST_MAC_3              0x44
#define DST_MAC_2              0x33
#define DST_MAC_1              0x22
#define DST_MAC_0              0x11

#define TCP_HEAD_LEN           20
#define UDP_HEAD_LEN           8
#define IP_HEAD_LEN            20
#define ETH_HEAD_LEN           14

#define PROTOCOL_UDP           0x11
#define PROTOCOL_TCP           0x06

#define TCP_ISS_CLIENT         0x10000      // initial sequence number for TCP client
#define TCP_ISS_SERVER         0x20000      // initial sequence number for TCP server
#define TCP_MAX_WINDOW         0xFFFFF      // initial TCP send window

#define PAYLOAD_MAX_LEN        1500         // max length for an UDP packet / TCP segment

#define TCP_FLAG_FIN           0x01
#define TCP_FLAG_SYN           0x02
#define TCP_FLAG_RST           0x04
#define TCP_FLAG_PSH           0x08
#define TCP_FLAG_ACK           0x10
#define TCP_FLAG_URG           0x20

#define NIC_RX_BUF_SIZE        0x100000     // 1 Mbytes
#define NIC_R2T_QUEUE_SIZE     0x64         // smallest KCM size
#define NIC_CRQ_QUEUE_SIZE     0x8          // actual size is 8 * sizeof(sockaddr_t)
#define NIC_KERNEL_BUF_SIZE    0x800        // 2 Kbytes for one ETH/IP/TCP packet

/*****************************************************************************************
 * This structure defines the specific chdev extension for NIC device:
 * - queue : local pointer on the memory mapped queue of TX or RX packets, used
 *   by the NIC driver to move packets to/from the NIC hardware. The actual descriptor
 *   depends on the NIC implementation.
 * - root  : root of an xlist of sockets that are in the LISTEN state, waiting one or
 *   several TCP connection requests from remote processes. It is only used by the
 *   NIC_RX server thread attached to a NIC_RX chdev. 
 * - lock  : lock protecting concurrent access to the litening sockets list.
 ****************************************************************************************/

typedef struct nic_extend_s
{
    void            * queue;    /*! pointer on NIC packets queue descriptor (RX or TX)  */
    xlist_entry_t     root;     /*! root of listening sockets list (only used in RX[0]) */
    remote_busylock_t lock;     /*! lock protecting this list  (only used in RX[0]      */
}
nic_extend_t;

/*****************************************************************************************
 * This enum defines the various implementations of the generic NIC peripheral.
 * This array must be kept consistent with the define in the arch_info.h file.
 ****************************************************************************************/

typedef enum nic_impl_e
{
    IMPL_NIC_CBF =   0,     
    IMPL_NIC_I86 =   1,
}
nic_impl_t;

/****************************************************************************************
 * This defines the (implementation independant) commands to access the NIC hardware.
 * There is two types of commands
 * - The first 2 commands are used by the NIC_TX and NIC_RX server threads, and stored
 *   in the server thread descriptor, to access the NIC_RX & NIC_TX packet queues.
 *   The buffer is always a 2K bytes kernel buffer, containing an Ethernet packet.
 * - The next 4 synchronous commands are used by the client th, and stored in the 
 *   client thread descriptor, to directly access the NIC registers. 
 ****************************************************************************************/

typedef enum nic_cmd_e
{
    NIC_CMD_WRITE        = 10,  /*! put one (given length) packet to TX queue           */
    NIC_CMD_READ         = 11,  /*! get one (any length) packet from RX queue           */

    NIC_CMD_GET_KEY      = 20,  /*! return channel index from IP address and port       */ 
    NIC_CMD_SET_RUN      = 21,  /*! enable/disable one NIC channel                      */
    NIC_CMD_GET_INSTRU   = 22,  /*! return one intrumentation register value            */
    NIC_CMD_CLEAR_INSTRU = 23,  /*! reset all instrumentation registers                 */
}
nic_cmd_t;

typedef struct nic_command_s
{
    xptr_t      dev_xp;       /*! extended pointer on NIC chdev descriptor              */
    nic_cmd_t   type;         /*! command type                                          */
    uint8_t   * buffer;       /*! local pointer on kernel buffer                        */  
    uint32_t    length;       /*! number of bytes in buffer                             */
    uint32_t    status;       /*! return value (depends on command type)                */
    uint32_t    error;        /*! return an error from the hardware (0 if no error)     */
}
nic_command_t;

/******************************************************************************************
 * This function completes the NIC-RX and NIC-TX chdev descriptors initialisation.
 * namely the link with the implementation specific driver.
 * The func, impl, channel, is_rx, base fields have been previously initialised.
 * It calls the specific driver initialisation function, to initialise the hardware
 * device and the specific data structures when required.
 * It creates the associated server thread and allocates a WTI from local ICU.
 * For a TX_NIC chedv, it allocates and initializes the R2T queue used by the 
 * NIC_RX[channel] server to send direct requests to the NIC_TX[channel] server,
 * and the CRQ queue used to register connection requests.
 * It must de executed by a local thread.
 * For NIC_TX and NIC_RX chdevs, the "wait_root" field is actually a list of sockets.
 ******************************************************************************************
 * @ chdev    : local pointer on NIC chdev descriptor.
 *****************************************************************************************/
void dev_nic_init( struct chdev_s * chdev );

/* Functions directly called by a client thread in any cluster */
 
/******************************************************************************************
 * This function compute a channel index in range [0,nic_channels[ from the remote IP
 * address <addr> and <port>, by calling the relevant driver command.
 ******************************************************************************************
 * @ addr     : [in] IP address.
 * @ port     : [in] TCP/UDP port.
 * @ return the selected channel index
 *****************************************************************************************/
uint32_t dev_nic_get_key( uint32_t addr,
                          uint16_t port );

/******************************************************************************************
 * This function activate / de-activate a NIC channel DMA engine identified by the
 * <channel> argument, as defined by the <run> argument.
 ******************************************************************************************
 * @ channel  : [in] NIC channel index.
 * @ run      : [in] activate if non-zero / desactivate if zero.
 * @ return 0 if success / return -1 if error.
 *****************************************************************************************/
error_t dev_nic_set_run( uint32_t channel,
                         uint32_t run );

/******************************************************************************************
 * This instrumentation function displays on the TXT0 kernel terminal the content
 * of the instrumentation registers contained in the NIC device.
 ******************************************************************************************
 * @ return 0 if success / return -1 if error.
 *****************************************************************************************/
error_t dev_nic_get_instru( void );

/******************************************************************************************
 * This instrumentation function reset all instrumentation registers contained
 * in the NIC device.
 ******************************************************************************************
 * @ return 0 if success / return -1 if error.
 *****************************************************************************************/
error_t dev_nic_clear_instru( void );

/* Functions executed by the TX and RX server threads */ 
 
/******************************************************************************************
 * This function is executed by the server thread associated to a NIC_TX[channel] chdev. 
 * This TX server thread is created by the dev_nic_init() function.
 * It build and send UDP packets or TCP segments for all clients threads registered in
 * the NIC_TX[channel] chdev. The command types are (CONNECT / SEND / CLOSE), and the
 * priority between clients is round-robin. It takes into account the request registered
 * by the RX server thread in the R2T queue associated to the involved socket.
 * When a command is completed, it unblocks the client thread. For a SEND command, the 
 * last byte must have been sent for an UDP socket, and it must have been acknowledged
 * for a TCP socket.
 * When the TX client threads queue is empty, it blocks on THREAD_BLOCKED_CLIENT
 * condition and deschedules. It is re-activated by a client thread registering a command.
 * When the NIC_TX packet queue is full, it blocks on the THREAD_BLOCKED_ISR condition
 * and deschedules. It is reactivated by the NIC_TX DMA engine.
 ******************************************************************************************
 * Implementation note:
 * It execute an infinite loop in which it takes the lock protecting the clients list
 * to build a "kleenex" list of currently registered clients.
 * For each client registered in this "kleenex" list, it takes the lock protecting the
 * socket state, build one packet/segment in a local 2K bytes kernel buffer, calls the 
 * transport layer to add the UDP/TCP header, calls the IP layer to add the IP header, 
 * calls the ETH layer to add the ETH header, and moves the packet to the NIC_TX_QUEUE.
 * Finally, it updates the socket state, and release the socket lock.
 ******************************************************************************************
 * @ chdev    : [in] local pointer on one local NIC_TX[channel] chdev descriptor.
 *****************************************************************************************/
void dev_nic_tx_server( struct chdev_s * chdev );


/******************************************************************************************
 * This function is executed by the server thread associated to a NIC_RX[channel] chdev. 
 * This RX server thread is created by the dev_nic_init() function.
 * It handles all UDP packets or TCP segments received by the sockets attached to 
 * the NIC_RX[channel] chdev. It writes the received data in the socket rcv_buf, and
 * unblocks the client thread waiting on a RECV command.
 * To implement the three steps handshahke required by a TCP connection, it posts direct
 * requests to the TX server, using the R2T queue attached to the involved socket. 
 * It blocks on the THREAD_BLOCKED_ISR condition and deschedules when the NIC_RX_QUEUE
 * is empty, and is re-activated by the NIC_RX_ISR, when the queue becomes non empty.
 ******************************************************************************************
 * Implementation note:
 * It executes an infinite loop in which it extracts one packet from the NIC_RX_QUEUE
 * of received packets, copies this packet in a local 2 kbytes kernel buffer, checks 
 * the Ethernet header, checks the IP header, calls the relevant (TCP or UDP) transport
 * protocol that search a matching socket for the received packet. It copies the payload 
 * to the relevant socket rcv_buf when the packet is acceptable, and unblocks the client
 * thread. It discard the packet if no socket found.
 ******************************************************************************************
 * @ chdev    : [in] local pointer on one local NIC_RX[channel] chdev descriptor.
 *****************************************************************************************/
void dev_nic_rx_server( struct chdev_s * chdev );

/******************************************************************************************
 * This function displays all the fields of an ETH/IP/TCP segment or ETH/IP/UDP packet. 
 ******************************************************************************************
 * @ is_tx   : [in] sent packet if true / received packet if false.
 * @ pid     : [in] process identifier.
 * @ trdid   : [in] thread identifier.
 * @ cycle   : [in] date (number of cycles).
 * @ buf     : [in] local pointer on kernel buffer containing the packet.
 *****************************************************************************************/
void dev_nic_packet_display( bool_t    is_tx,
                             pid_t     pid,
                             trdid_t   trdid,
                             uint32_t  cycle,
                             uint8_t * buf );

#endif	/* _DEV_NIC_H */
