/*
 * dev_fbf.h - FBF (Frame Buffer) 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_FBF_H
#define _DEV_FBF_H

#include <hal_kernel_types.h>
#include <shared_fbf.h>
#include <remote_rwlock.h>
#include <bits.h>

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

struct chdev_s;

/*****************************************************************************************
 *      Frame Buffer Controler API definition
 *
 * This device provide access to an external graphic display, that is seen
 * as a fixed size frame buffer, mapped in the kernel address space.
 * The only pixel encoding type in the current ALMOS-MKH implementation (oct 2020)
 * is one byte per pixel (256 levels of gray).
 *
 * It supports a first API, for the user syscalls, implementing a simple windows manager.
 * This windows manager allows any process to create and use for display one (or several) 
 * window(s). Each window defines a private buffer, dynamically allocated in user space,
 * that can be directly accessed by the owner process.
 * These windows can be moved in the frame buffer, they can be resized, they can overlap
 * other windows, but a window must be entirely contained in the frame buffer.
 *
 * To avoid contention, the window descriptor, and the associated user buffer are not
 * allocated in the cluster containing the FBF chdev, but are distributed: each window
 * is allocated in the cluster defined by the thread that required the window creation.
 *
 * Each window has a single process owner, but all the windows are registered in the FBF
 * chdev as a windows_tbl[] array, indexed by the window identifier (wid), and each entry
 * contains an extended pointer on the window descriptor. All windows are also registered
 * in a trans-cluster xlist, defining the overlapping order (last window in xlist has the
 * highest priority). 
 *
 * To refresh a window <wid>, the owner process calls the dev_fbf_refresh_window()
 * function, that sends parallel RPC_FBF_DISPLAY requests to other cores. All cores
 * synchronously execute the dev_fbf_display() function. This function scan all windows
 * to respect the overlaping order, and updates all pixels of the <wid> window.
 * 
 * 1) This "syscall" API defines five syscalls : 
 * - FBF_GET_CONFIG     : returns the FBF width, height, and pixel encoding type.
 * - FBF_CREATE_WINDOW  : create a new window , owned by the calling process.
 * - FBF_DELETE_WINDOW  : delete a registered window. 
 * - FBF_MOVE_WINDOW    : move in FBF a registered window.
 * - FBF_REFRESH_WINDOW : request refresh of a given window.
 *
 * These 5 operations do not use the FBF device waiting queue, the associated
 * device thread and the FBF IRQ, as the client thread does NOT deschedule,
 * and does NOT call the FBF driver.
 *
 * 2) Two extra syscalls exist but are deprecated:
 * - FBF_DIRECT_WRITE   : move synchronously pixels from an user buffer to the FBF.
 * - FBF_DIRECT_READ    : move synchronously pixels from the FBF to an user buffer.
 * For these deprecated operations, the client thread calls
 * directly the driver to move data between the user buffer and the FBF.
 * 
 * 3) The FBF device defines defines four command types to access FBF driver(s) :
 * - FBF_DRIVER_KERNEL_WRITE : move pixels from a kernel window to the FBF.
 * - FBF_DRIVER_KERNEL_READ  : move pixels from the FBF to a kernel window.
 * - FBF_DRIVER_USER_WRITE   : move bytes from an user buffer to the FBF. 
 * - FBF_DRIVER_USER_READ    : move bytes from the FBF to an user buffer. 
 *
 * Note: As we don't use any external DMA to move data to or from the frame buffer,
 * but only software memcpy, there is no L2/L3 coherence issue for this device.
 *
 *****************************************************************************************/

/******************************************************************************************
 * This defines the (implementation independant) extension for the generic FBF device.
 *****************************************************************************************/

typedef struct fbf_extend_s
{
    remote_rwlock_t windows_lock;        /*! lock protecting windows xlist               */
    xlist_entry_t   windows_root;        /*! root of the windows xlist                   */

    xptr_t          windows_tbl[CONFIG_FBF_WINDOWS_MAX_NR];         /*! window desc.     */
    bitmap_t        windows_bitmap[CONFIG_FBF_WINDOWS_MAX_NR >> 5]; /*! wid allocator    */

    uint32_t        width;               /*! Frame Buffer number of pixels per line.     */
    uint32_t        height;              /*! Frame Buffer total number of lines.         */
    uint32_t        subsampling;         /*! Frame Buffer pixel encoding type.           */
}
fbf_extend_t;

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

typedef enum
{
    IMPL_FBF_SCL =   0,     
    IMPL_FBF_I86 =   1,  
} 
fbf_impl_t;

/******************************************************************************************
 * This structure defines the FBF command for all drivers implementing the FBF device.
 *****************************************************************************************/

typedef enum
{
    FBF_DRIVER_USER_READ     = 1,
    FBF_DRIVER_USER_WRITE    = 2,
    FBF_DRIVER_KERNEL_READ   = 3,
    FBF_DRIVER_KERNEL_WRITE  = 4,
}
fbf_driver_cmd_type_t;

typedef struct fbf_command_s
{
    xptr_t      dev_xp;        /*! extended pointer on device descriptor                 */
    uint32_t    type;          /*! requested driver operation type.                      */
    uint32_t    npixels;       /*! number of bytes.                                      */
    uint32_t    offset;        /*! offset in frame buffer (pixels)                       */
    void      * buffer;        /*! pointer on memory buffer (kernel or user)             */
    uint32_t    error;         /*! operation status (0 if success)                       */
}
fbf_command_t;

/******************************************************************************************
 * This structure defines an FBF window descriptor, allocated to a given user process.
 * The window descriptor and the associated buffer are allocated in the cluster where
 * is running the thread requesting the window creation.
 * The <wid> allocator, the window_tbl[] array, and the root of the xlist of windows are
 * imlemented in the FBF device extension.
 *****************************************************************************************/

typedef struct fbf_window_s
{
    pid_t          pid;         /*! owner process identifier                             */
    uint32_t       wid;         /*! window identifier                                    */
    uint32_t       height;      /*! number of lines in window                            */
    uint32_t       width;       /*! number of pixels per line in window                  */
    uint32_t       l_min;       /*! first line index in FBF                              */
    uint32_t       p_min;       /*! first pixel index in FBF                             */
    uint8_t      * buffer;      /*! pointer on buffer in user space                      */
    bool_t         hidden;      /*! no display on FBF when true                          */
    xlist_entry_t  xlist;       /*! member of registered FBF windows list                */
}
fbf_window_t;


/******************************************************************************************
 * This function returns a printable string for a given FBF user command  <cmd_type>.
 * WARNING : It must be kept consistent with the enum in the <shared_fbf.h> file
 ******************************************************************************************
 * @ cmd_type   :  FBF user command type (defined in shared_fbf.h file).
 * @ returns a string pointer.
 *****************************************************************************************/
char * dev_fbf_cmd_str( uint32_t cmd_type );

/******************************************************************************************
 * This function checks that the calling process is the owner of the window identified
 * by the <wid> argument, and returns an extended pointer on the window descriptor.
 * It can be called by a thread running in any cluster.
 ******************************************************************************************
 * @ wid        :  FBF window kernel identifier.
 * @ returns XPTR on the window if success / return XPT_NULL if not owner or undefined.
 *****************************************************************************************/
xptr_t dev_fbf_get_xptr_from_wid( uint32_t wid );

/******************************************************************************************
 * This function completes the FBF chdev descriptor initialisation.
 * It calls the specific driver initialisation function, to initialise the hardware
 * device, and the chdev extension. It must be called by a local thread.
 ******************************************************************************************
 * @ chdev      : pointer on FBF chdev descriptor.
 *****************************************************************************************/
void dev_fbf_init( struct chdev_s * chdev );

/******************************************************************************************
 * This function implements the fbf_get_config() syscall, and returns the FBF
 * size and type. It can be called by a client thread running in any cluster.
 * It does NOT access the hardware, as the size and type have been registered
 * in the chdev descriptor extension by the dev_fbf_init() function.
 * It can be called by any thread running in any cluster.
 ******************************************************************************************
 * @ width     : [out] number of pixels per line.
 * @ height    : [out] total number of lines.
 * @ type      : [out] pixel encoding type.
 *****************************************************************************************/
void dev_fbf_get_config( uint32_t  * width,
                         uint32_t  * height,
                         uint32_t  * type );

/******************************************************************************************
 * This function implements the fbf_create_window() syscall.
 * It registers a new window in the windows_tbl[] array, and the windows list,
 * registers in the reference cluster an ANON vseg, that will be mapped in local cluster. 
 * The window index <wid> is dynamically allocated. The owner is the calling process.
 * The FBF window is defined by the <nlines>, <npixels>, <l_min>, <p_min> arguments.
 * It can be called by any thread running in any cluster. As the vseg is not directly 
 * mapped to the frame buffer, the owner process can access this private buffer without
 * syscall. As for any vseg, the physical memory is allocated on demand at each page fault.
 * The created vseg base address in user space is returned in the <user_base> argument.
 * The created window is set in "hidden" mode.
 ******************************************************************************************
 * Implementation note:
 * 1. it allocates memory in the local cluster for the window descriptor,
 * 2. it creates in the associated vseg,
 * 3. it initializes the window descriptor,
 * 4. it takes the lock protecting the windows in write mode,
 * 5. it allocates a new  <wid>,
 * 6. it registers the window in the window_tbl[] array,
 * 7. it registers the window in the FBF rooted windows list,
 * 8. it releases the lock protecting windows.
 ******************************************************************************************
 * @ nlines      : [in]  number of lines in window.
 * @ npixels     : [in]  number of pixels per line in window.
 * @ l_min       : [in]  first pixel index in FBF reference.
 * @ p_min       : [in]  first line index in FBF reference.
 * @ user_base   : [out] pointer on allocated buffer base in user space.
 * @ return the <wid> index if success / returns -1 if failure
 *****************************************************************************************/
uint32_t dev_fbf_create_window( uint32_t   nlines,
                                uint32_t   npixels,
                                uint32_t   l_min,
                                uint32_t   p_min,
                                intptr_t * user_base );


/******************************************************************************************
 * This function implements the fbf_active_wiindow syscall. It set or reset the "hidden"
*  flag for the window identified by the <wid> argument as specified by <active> argument.
 ******************************************************************************************
 * @ wid       : [in] window index in window_tbl[].
 * @ active    : [in] set hidden flag if zero / rest hidden flag if non zero.
 * @ returns 0 if success / returns -1 if wid not registered.
 *****************************************************************************************/
error_t dev_fbf_active_window( uint32_t wid,
                               uint32_t active );

/******************************************************************************************
 * This function implements the fbf_delete_window() syscall to delete a FBF window,
 * and release all memory allocated for this window and for the associated vseg.
 * releases the memory allocated for the window buffer and for the window descriptor.
 * It can be called by any thread running in any cluster.
 ******************************************************************************************
 * Implementation note:
 * 1. it set the "hidden" flag in the window descriptor,
 * 2. it refresh the window in FBF,
 * 3. it takes the lock protecting windows in write mode,
 * 4. it removes the window from windows_tbl[] array,
 * 5. it removes the window from xlist,      
 * 6. it releases the wid to bitmap,
 * 7. it releases the lock protecting windows from write mode,
 * 8. it releases the memory allocated for window descriptor,
 * 9. it deletes the associated vseg in all clusters
 ******************************************************************************************
 * @ wid       : [in] window index in window_tbl[].
 * @ returns 0 if success / returns -1 if wid not registered.
 *****************************************************************************************/
error_t dev_fbf_delete_window( uint32_t wid );
                                
/******************************************************************************************
 * This function implements the fbf_move_window() syscall.
 * It moves a window identified by the <wid> argument to a new position in the FBF,
 * defined by the <l_min> and <p_min> arguments.
 * It can be called by any thread running in any cluster.
 ******************************************************************************************
 * Implementation note:
 * 1. it set the "hidden" flag in window descriptor,
 * 2. it refresh the FBF for the current window position,
 * 3. it takes the lock protecting windows in write mode,
 * 4. it set the new coordinates in the window descriptor,
 * 5. it gives the modified window the highest priority,
 * 6. it releases the lock protecting windows,
 * 7. it reset the "hidden" flag in window descriptor,
 * 8. it refresh the FBF for the new window position,
 ******************************************************************************************
 * @ wid       : [in] window index in window_tbl[].
 * @ l_min     : [in] new first pixel index in FBF reference.
 * @ p_min     : [in] new first line index in FBF reference.
 * @ returns 0 if success / returns -1 if illegal arguments.
 *****************************************************************************************/
error_t dev_fbf_move_window( uint32_t wid,
                             uint32_t l_min,
                             uint32_t p_min );

/******************************************************************************************
 * This function implements the fbf_resize_window() syscall.
 * It changes the <width> and <height> of a window identified by the <wid> argument.
 * It updates the associated vseg "size" if required, but does not change the vseg "base".
 * When the new window buffer is larger than the existing one, it is 0 filled.
 * It can be called by any thread running in any cluster.
 ******************************************************************************************
 * Implementation note:
 * 1.  it set the "hidden" flag in window descriptor,
 * 2.  it refresh the FBF for the current window,
 * 3.  it takes the lock protecting windows in write mode,
 * 4.  it set the new size in the window descriptor,
 * 5.  it resizes the associated vseg if required,
 * 6.  it fill the user buffer with zero if required,
 * 7.  it gives the modified window the highest priority,
 * 8.  it releases the lock protecting windows,
 * 9.  it reset the "hidden" flag in window descriptor,
 * 10. it refresh the FBF for the new window,
 ******************************************************************************************
 * @ wid       : [in] window index in window_tbl[].
 * @ width     : [in] new number of pixels per line.
 * @ height    : [in] new number of lines.
 * @ returns 0 if success / returns -1 if illegal arguments.
 *****************************************************************************************/
error_t dev_fbf_resize_window( uint32_t wid,
                               uint32_t width,
                               uint32_t height );

/******************************************************************************************
 * This function implements the fbf_refresh_window() syscall.
 * It allows an owner process to signal the windows manager that some lines of a window
 * identified by the <wid>, <line_min>, and <line_max> argument have been modified, and
 * must be refreshed in the FBF.
 * It can be called by any thread running in any cluster.
 ******************************************************************************************
 * Implementation note:
 * it simply checks the arguments, and calls the fbf_update() function.
 ******************************************************************************************
 * @ wid        : [in] window index in window_tbl[]
 * @ line_min   : [in] first line index in window in window referencd.
 * @ line_max   : [in] last line index in window in window reference (excluded).
 * @ returns 0 if success / returns -1 if wid not registered.
 *****************************************************************************************/
error_t dev_fbf_refresh_window( uint32_t wid,
                                uint32_t line_min,
                                uint32_t line_max );

/******************************************************************************************
 * This function implements the fbf_front_window() syscall.
 * It gives the highest priority to the window identified by the <wid> argument,
 * and refresh the FBF accordingly.
 * It can be called by any thread running in any cluster.
 ******************************************************************************************
 * Implementation note:
 * 1. it takes the lock protecting windows in write mode,
 * 2. it modify the xlist of windows rooted in FBF,
 * 3. it releases the lock from write mode,
 * 4. it refresh the window in FBF,
 ******************************************************************************************
 * @ wid        : [in] window index in window_tbl[]
 * @ returns 0 if success / returns -1 if wid not registered.
 *****************************************************************************************/
error_t dev_fbf_front_window( uint32_t wid );

/******************************************************************************************
 * This function displays on the TXT0 kernel terminal the list of registered FBF windows
 * rooted in the FBF device - in decreasing priority order - owned by the process
 * identified by the <pid> argument. It displays all windows when pid value is 0.
 * It can be called by any thread running in any cluster.
 ******************************************************************************************
 * @ pid       : [in] target process identifier / all processes when pid == 0
 *****************************************************************************************/
void dev_fbf_display_windows( pid_t pid );

/******************************************************************************************
 * This function deletes all FBF windows owned by the process identified by 
 * the <pid> argument. It can be called by any thread running in any cluster.
 ******************************************************************************************
 * @ pid       : [in] target process identifier.
 *****************************************************************************************/
void dev_fbf_cleanup( pid_t pid );







/******************************************************************************************
 * TODO : This function is deprecated ( january 2020 [AG] ). It was defined 
 * to implement the fbf_read() and fbf_write() deprecated syscalls.
 ******************************************************************************************
 * It  moves <length> bytes between the frame buffer, starting from pixel defined
 * by the <offset> argument, and an user buffer defined by the <user_buffer> argument.
 * The transfer direction are defined by the <is_write> argument.
 * An error is returned when <offset> + <npixels> is larger than the FBF size.
 * The request is registered in the client thread descriptor, but the client thread is
 * not descheduled, and calls directly the FBF driver.
 * It can be called by a client thread running in any cluster.
 ******************************************************************************************
 * @ is_write    : [in] write FBF weh true / read FBF when false
 * @ user_buffer : [in] pointer on memory buffer in user space.
 * @ npixels     : [in] number of bytes.
 * @ offset      : [in] first byte in frame buffer.    
 * @ returns 0 if success / returns -1 if error.
 *****************************************************************************************/
error_t dev_fbf_move_data( bool_t     is_write,
                           void     * user_buffer,
                           uint32_t   npixels,
                           uint32_t   offset );

#endif	/* _DEV_FBF_H */
