/*
 * dev_nic.h - NIC (Network Controler) generic device API definition.
 * 
 * Author  Alain Greiner    (2016)
 *
 * 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 <almos_config.h>
#include <hal_types.h>

/*****************************************************************************************
 *     Generic Network Interface Controler definition
 *
 * This device provide access to an external Gigabit Ethernet network controler.
 * It assume that the NIC hardware peripheral handles two packets queues for sent (TX)
 * and received (RX) packets. Packets are (Ethernet/IPV4).
 * 
 * The NIC device is handling an (infinite) stream 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 hardware NIC controller can optionnally implement
 * multiple channels:
 * - The RX channels are indexed by an hash key derived from the source IP address.
 * - The TX channels are indexed by an hash key derived from the destination IP address.
 * These 2*N devices, and 2*N associated server threads, are distributed in 2*N clusters.
 * The  2*N server threads implement the protocols stack. The RX server threads block
 * and deschedule when the RX queue is empty. The TX server stack block and deschedule
 * when the queue is full. 
 *
 * It is the driver responsibily to re-activate a blocked server thread when
 * the queue state is modified: not full for TX, or not empty for RX.
 *
 * 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 device descriptor and server thread.
 * to simplify the server thread re-activation.
 *
 * Finally, the generic NIC device API defines the following commands:
 * - READABLE : returns true if at least one RX paquet is available in RX queue.
 * - WRITABLE : returns true if atleast one empty slot is available in TX queue.
 * - READ     : consume one packet from the RX queue.
 * - WRITE    : produce one packet fto the 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 structure pkd_t
 * defining a packet descriptor is defined below, and contain the buffer pointer
 * and the actual Ethernet packet Length.
 *
 * The actual TX an RX queues structures depends on the hardware NIC implementation,
 * and are defined in the driver code. 
 *****************************************************************************************/

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

struct chdev_s;

/******************************************************************************************
 * This defines the extension for the generic IOC device.
 * The actual queue descriptor depends on the implementation.
 *****************************************************************************************/

typedef struct nic_extend_s
{
    void     * queue;     /*! local pointer on the packets queue descriptor (RX or TX)   */
}
nic_extend_t;

/******************************************************************************************
 * This structure defines the Ethernet/IPV4 packet descriptor, that is sent to,
 * or received from, the protocols stack.
 *****************************************************************************************/

typedef struct pkd_s
{
    char      * buffer;   /*! local pointer on 2 Kbytes buffer containing packet         */  
    uint32_t    length;   /*! actual number of bytes                                     */
}
pkd_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.
 *****************************************************************************************/

enum nic_impl_e
{
    IMPL_NIC_SOC =   0,     
    IMPL_NIC_I86 =   1,
}
nic_impl_t;

/******************************************************************************************
 * This defines the (implementation independant) command  passed to the NIC driver.
 *****************************************************************************************/

typedef enum nic_cmd_e
{
    NIC_CMD_WRITABLE = 0,  /*! test TX queue not full (for a given length packet)        */
    NIC_CMD_WRITE    = 1,  /*! put one (given length) packet to TX queue                 */
    NIC_CMD_READABLE = 2,  /*! test RX queue not empty (for any length packet)           */
    NIC_CMD_READ     = 3,  /*! get one (any length) packet from RX queue                 */
}
nic_cmd_t;

typedef struct nic_command_s
{
    xptr_t      dev_xp;   /*! extended pointer on device descriptor                      */
    nic_cmd_t   cmd;      /*! requested operation type                                   */
    char      * buffer;   /*! local pointer on 2 Kbytes buffer containing packet         */  
    uint32_t    length;   /*! actual number of bytes                                     */
    bool_t      status;   /*! return true if writable or readable (depend on command)    */
    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.
 * It must de executed by a local thread.
 ******************************************************************************************
 * @ chdev    : local pointer on NIC chdev descriptor.
 *****************************************************************************************/
void dev_nic_init( struct chdev_s * chdev );

/******************************************************************************************
 * This blocking function must be called by the kernel thread running in the cluster
 * containing the NIC_RX channel device descriptor.
 * It read one packet (Ethernet/IPV4) from the NIC_RX queue associated to the NIC channel.
 * It calls directly the NIC driver, without registering in a waiting queue, because 
 * only this NIC_RX thread can access this packets queue.
 * 1) It test the packets queue status, using the NIC_CMD_WRITABLE command.
 *    If it is empty, it unmask the NIC-RX channel IRQ, blocks and deschedule.
 *    It is re-activated by the NIC-RX ISR (generated by the NIC) as soon as the queue
 *    becomes not empty.
 * 2) if the queue is not empty, it get one packet, using the driver NIC_CMD_READ command.
 * Both commands are successively registered in the NIC-RX server thread descriptor
 * to be passed to the driver. 
 *
 * WARNING : for a RX packet the initiator is the NIC hardware, and the protocols
 * stack is traversed upward, from the point of view of function calls.
 ******************************************************************************************
 * @ pkd     : pointer on packet descriptor (expected).
 * @ returns 0 if success / returns non zero if ENOMEM, or error reported from NIC.
 *****************************************************************************************/
error_t dev_nic_read( pkd_t * pkd );

/******************************************************************************************
 * This blocking function must be called by the kernel thread running in the cluster
 * containing the NIC_TX channel device descriptor.
 * It writes one packet (Ethernet/IPV4) to the NIC_RX queue associated to the NIC channel.
 * It calls directly the NIC driver, without registering in a waiting queue, because 
 * only this NIC_TX thread can access this packets queue.
 * 1) It test the packets queue status, using the NIC_CMD_READABLE command.
 *    If it is full, it unmask the NIC-TX channel IRQ, blocks and deschedule.
 *    It is re-activated by the NIC-TX ISR (generated by the NIC) as soon as the queue
 *    is not full.
 * 2) If the queue is not empty, it put one packet, using the driver NIC_CMD_WRITE command.
 * Both commands are successively registered in the NIC-TX server thread descriptor
 * to be passed to the driver. 
 *
 * WARNING : for a TX packet the initiator is the "client" thread, and the protocols
 * stack is traversed downward from the point of view of function calls.
 ******************************************************************************************
 * @ pkd     : pointer on packet descriptor (to be filed).
 * @ returns 0 if success / returns if length > 2K, undefined key, or error from NIC.
 *****************************************************************************************/
error_t dev_nic_write( pkd_t * pkd );


/******************************************************************************************
 * This function is executed by the server thread associated to a NIC channel device 
 * descriptor (RX or TX). This thread is created by the dev_nic_init() function.
 * It executes an infinite loop, handling one packet per iteration.
 *
 * -- For a TX channel --
 * 1) It allocates a 2 Kbytes buffer.
 * 2) It copies the client TCP/UDP packet in this buffer.
 * 3) It calls the IP layer to add the IP header.
 * 4) It calls the ETH layer to add the ETH header.
 * 5) It calls the dev_nic_write() blocking function to move the packet to the TX queue.
 * 6) It releases the 2 Kbytes buffer.
 *
 * When the waiting threads queue is empty, it blocks on the THREAD_BLOCKED_IO_CMD
 * condition and deschedule. It is re-activated by a client thread registering a command.
 *
 * -- For a RX channel --
 * 1) It allocates a 2 Kbytes buffer.
 * 2  It calls the dev_nic_read() blocking function to move the ETH packet to this buffer.
 * 3) It calls the ETH layer to analyse the ETH header.
 * 4) It calls the IP layer to analyse the IP header. TODO ???
 * 5) It calls the transport (TCP/UDP) layer. TODO ???
 * 5) It deliver the packet to the client thread. TODO ???
 * 6) It releases the 2 Kbytes buffer.
 *
 * When the RX packets queue is empty, it blocks on the THREAD_BLOCKED_IO_CMD
 * condition and deschedule. It is re-activated by the NIC driver when this queue
 * becomes non empty. 
 ******************************************************************************************
 * @ dev     : local pointer on NIC chdev descriptor.
 *****************************************************************************************/
void dev_nic_server( struct chdev_s * chdev );


#endif	/* _DEV_NIC_H */
