/*
 * device.h - kernel definition of a device
 * 
 * Authors  Ghassan Almaless (2008,2009,2010,2011,2012)
 *          Mohamed Karaoui  (2015)
 *          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-MKH; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef _DEVICE_H_
#define _DEVICE_H_

#include <almos_config.h>
#include <hal_types.h>
#include <xlist.h>
#include <metafs.h>
#include <remote_spinlock.h>
#include <dev_ioc.h>
#include <dev_nic.h>
#include <dev_icu.h>
#include <dev_pic.h>

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

struct  device_s;
struct  thread_s;
struct  boot_info_s;

/******************************************************************************************
 * These macros extract the functionality and the implementation from the peripheral type.
 *****************************************************************************************/
 
#define FUNC_FROM_TYPE( type )    ((uint32_t)(type>>16))
#define IMPL_FROM_TYPE( type )    ((uint32_t)(type & 0x0000FFFF))

/******************************************************************************************
 * This define the generic prototypes for the three functions that must be defined
 * by all drivers :
 * - "init"    : device initialisation.
 * - "command" : start an I/O operation.
 * - "isr"     : complete an I/O operation.
 * The "init" function is called by kernel_init() to initialise the hardware device.
 * The "cmd" and "isr" are registered in the generic device descriptor by kernel_init(),
 * and are called to start and complete an I/O operation.  
*****************************************************************************************/

typedef void (dev_ini_t) ( xptr_t dev );      
typedef void (dev_cmd_t) ( xptr_t thread );  
typedef void (dev_isr_t) ( struct device_s * dev );  

/******************************************************************************************
 * This enum defines the supported generic device types.
 * These types are functionnal types: all (architecture specific) implementations 
 * provide the same set of operations and the same driver API.
 * This enum must be kept consistent with the enum in arch_info.h file
 *****************************************************************************************/
 
enum dev_func_type 
{
	DEV_FUNC_RAM   =  0,     /*! Random Access Memory                                    */
	DEV_FUNC_ROM   =  1,     /*! Read Only Memory                                        */
	DEV_FUNC_TXT   =  2,     /*! Text Terminal Controler                                 */
    DEV_FUNC_FBF   =  3,     /*! Frame Buffer Controler                                  */
    DEV_FUNC_IOB   =  4,     /*! I/O Bridge Component                                    */
	DEV_FUNC_IOC   =  5,     /*! Block Device Controler                                  */
	DEV_FUNC_MMC   =  6,     /*! L2 cache Configuration                                  */
	DEV_FUNC_MWR   =  7,     /*! Hardware coprocessor                                    */
	DEV_FUNC_NIC   =  8,     /*! GMII Network Interface Controler                        */
	DEV_FUNC_CMA   =  9,     /*! Chained buffers DMA Controler                           */
	DEV_FUNC_ICU   = 10,     /*! Interrupt Controler Unit                                */
	DEV_FUNC_PIC   = 11,     /*! HWI to WTI translator                                   */

    DEV_FUNC_NR    = 12,
};

/******************************************************************************************
 * This structure defines a device descriptor.
 * There is one device descriptor per peripheral channel.
 * This structure is NOT replicated, and can be located in any cluster.
 * One kernel thread, in charge of handling the commands registered in the waiting queue
 * of client threads is associated to each device descriptor.
 * The device specific extensions are defined in the relevant device file.
 *****************************************************************************************/

typedef struct device_s
{
	uint32_t             func;        /*! peripheral functionnal type                    */
	uint32_t             impl;        /*! peripheral inplementation subtype              */
    uint32_t             channel;     /*! channel index                                  */
    bool_t               is_rx;       /*! relevant for NIC peripheral channels only      */
	xptr_t               base;        /*! extended pointer on channel segment            */
    uint32_t             size;        /*! channel_segment size                           */

    dev_cmd_t          * cmd;         /*! local pointer on driver command function       */
    dev_isr_t          * isr;         /*! local pointer on driver ISR function           */  
    struct thread_s    * server;      /*! local pointer on associated server thread      */

    uint32_t             irq_type;    /*! associated IRQ type in local ICU               */
    uint32_t             irq_id;      /*! associated IRQ index in local ICU              */

    metafs_t             node;        /*! Metafs node associated with this device        */
	char                 name[16];    /*! device name in file system                     */

	remote_spinlock_t    wait_lock;	  /*! lock protecting exclusive access to queue      */
    xlist_entry_t        wait_root;   /*! root of waiting threads queue                  */

    union
    {
        ioc_extend_t     ioc;         /*! IOC specific extension                         */
        nic_extend_t     nic;         /*! NIC specific extension                         */
        icu_extend_t     icu;         /*! ICU specific extension                         */
        pic_extend_t     pic;         /*! PIC specific extension                         */
    } 
    ext;
}
device_t;

/******************************************************************************************
 * This structure defines the devices descriptors directory.
 * Each entry in this structure contains an extended pointers on a device descriptor.
 * There is one entry per channel for an external peripheral.
 * There is one entry per cluster for an internal peripheral.
 * This structure exists in each cluster, and is initialised during kernel init.
 * It is used for fast access to a device descriptor, from type and channel for an
 * external peripheral, or from type and cluster for an internal peripheral.
 *****************************************************************************************/

typedef struct devices_directory_s
{
    xptr_t   txt[CONFIG_MAX_TXT_CHANNELS];
    xptr_t   ioc;
    xptr_t   nic_rx[CONFIG_MAX_NIC_CHANNELS];
    xptr_t   nic_tx[CONFIG_MAX_NIC_CHANNELS];
    xptr_t   pic;
    xptr_t   iob;
    xptr_t   icu[CONFIG_MAX_CLUSTERS];
    xptr_t   mmc[CONFIG_MAX_CLUSTERS];
    xptr_t   mwr[CONFIG_MAX_CLUSTERS];
}
devices_directory_t;

/******************************************************************************************
 * This structure defines the input IRQS for the PIC and ICU devices.
 * External peripherals IRQS (IOC, TXT, NIC) are connected to the PIC component, while
 * internal peripherals IRQs (MMC, MWR) are connected to the local ICU component.
 * Each entry in this structure contains the input IRQ index in PIC or ICU.
 * Value is -1 if the IRQ for a peripheral channel is not connected.
 * There is one entry per channel for an external peripheral.
 * There is one entry per cluster for an internal peripheral.
 * This structure exists in each cluster, and is initialised during kernel init.
 * It is mainly used for fast PIC configuration when the kernel start a new I/O 
 * operation to an external peripheral, as it gives the IRQ index from type and channel.
 *****************************************************************************************/

typedef struct devices_input_irq_s
{
    uint32_t   txt[CONFIG_MAX_TXT_CHANNELS];
    uint32_t   ioc;
    uint32_t   nic_rx[CONFIG_MAX_NIC_CHANNELS];
    uint32_t   nic_tx[CONFIG_MAX_CMA_CHANNELS];
    uint32_t   mmc[CONFIG_MAX_CLUSTERS];
    uint32_t   mwr[CONFIG_MAX_CLUSTERS];
}
devices_input_irq_t;



/******************************************************************************************
 * This  function allocates memory for a device descriptor.
 * - For a local (replicated) peripheral it allocates memory in local cluster.
 * - For an external peripheral, it uses the global variable "device_alloc_index"
 *   to select a remote cluster and send a RPC to make the allocation.
 * In both cases it returns an extended pointer on the device descriptor.
 ******************************************************************************************
 * @ info       : pointer on local boot_info structure.
 * @ is_local   : local (replicated) peripheral.
 * @ returns an extended pointer on device if success / returns XPTR_NUL if error.
 *****************************************************************************************/
xptr_t device_alloc( struct boot_info_s * info,
                     bool_t               is_local );

/******************************************************************************************
 * This  function initialises the basic fields from arguments values. 
 * The device specific fields are initialised later.
 * It uses remote_write accesses, and can be called in any cluster.
 ******************************************************************************************
 * @ dev       : extended pointer to device descriptor.
 * @ func      : functionnal type.
 * @ impl      : implementation type.
 * @ channel   : channel index / for multi-channels peripherals.
 * @ is_rx     : for NIC peripheral / NIC RX if true / NIC TX if false.
 * @ base      : extended pointer on channel segment base.
 * @ size      : channel segment size (bytes).
 *****************************************************************************************/
void device_init( xptr_t      dev,
                  uint32_t    func,
                  uint32_t    impl,
                  uint32_t    channel,
                  bool_t      is_rx,
                  xptr_t      base,
                  uint32_t    size );

/******************************************************************************************
 * This function registers a local client thread in the waiting queue of a remote 
 * device descriptor, activates (i.e. unblock) the server thread associated to device,
 * and blocks itself on the THREAD_BLOCKED_IO condition.
 ******************************************************************************************
 * @ dev     : extended pointer on remote device descriptor.
 * @ thread  : local pointer on client thread.
 *****************************************************************************************/
void device_register_command( xptr_t            xp_dev,
                              struct thread_s * thread );


#endif	/* _DEVICE_H_ */
